Guias

Kotlin e Java

Para mods grandes. A mesma API do CraftScript, com tipos estáticos, a IDE completa e qualquer biblioteca Java, além de algumas partes que, por enquanto, só existem aqui. Este guia usa Kotlin; Java funciona do mesmo jeito e aparece no final.

O que dá para fazer

Área Na API Em CraftScript
Itens, blocos, receitas ContentBuilder: item, block, shaped, shapeless, smelting Sim
Criaturas ContentBuilder.creature: corpo, textura, atributos, Behaviour, TargetRule, loot, surgimento natural, domesticação, criação, montaria, comportamentos próprios Sim
Sons e partículas ContentBuilder.sound, ContentBuilder.particle Sim
Geração de mundo ContentBuilder: ore, tree, patch, structure (pools, plan/build), biome, dimension, feature, terrain, biomes, carver, tag. Veja Geração de mundo e Geração por código Tudo menos feature, tag, salt e aboveBottom
Interface Form e player.showForm; server.bossBar; player.setSidebar Sim
Eventos Crafting.events.on<E> { }, com prioridade e cancelamento Sim
Efeitos, atributos, equipamento addEffect, attribute, setAttribute, equipment, setEquipment, EquipmentSlot Yes
Teclas ContentBuilder.key, PlayerKeyEvent Yes
Comandos Crafting.commands.command(…): subcomandos, argumentos tipados, permissões, aliases Sim
Mundo e jogadores World, Block, Entity, Player, Inventory, Server Sim
Tarefas Crafting.scheduler: runLater, runRepeating, runAsync, runSync Sem trabalho em segundo plano
Ajustes em arquivo Config tipada em TOML, pode sincronizar com os clientes Sim (config)
Rede Crafting.network.channel(…), com codecs personalizados Sim (listen, send)
Dados salvos player.data["rubies.visits"] = n, data.getInt(…); também em entidades, blocos e mundos Sim
Bibliotecas Java Qualquer uma Não

O que a API ainda não tem não existe em nenhuma linguagem. Não contorne isso chamando classes do Minecraft: o mod perderia a garantia de continuar funcionando quando o jogo muda.

Crie o projeto

Terminal
crafting new rubies --kind kotlin
cd rubies && gradle build

A API ainda não está em um repositório Maven público. Instale-a no seu Maven local a partir do código-fonte do Crafting e declare-a como compileOnly: o Crafting a fornece ao mod em tempo de execução.

Terminal
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) } // o Minecraft 26.x roda no Java 25

crafting new gera esse arquivo e um projeto já dividido por responsabilidade: <Name>Mod.kt, que só conecta as partes, content/ModContent.kt, events/Welcome.kt e commands/GemCommand.kt.

Estrutura

Pacotes por tema, uma classe de entrada que só conecta as partes e um arquivo por parte. O guia Organizando um mod explica por quê; aqui está o mod de exemplo completo:

EstruturaTexto
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/

A classe de entrada não faz nada sozinha: cria o que é compartilhado e chama cada parte.

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()
    }
}

O manifesto

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" }
}
Campo O que é
id 2–64 caracteres: letras minúsculas, dígitos, _ ou -, começando com uma letra. Também é o namespace dos seus itens e blocos.
kind mod ou plugin (só servidor, recarregável; veja abaixo).
side both, client ou server. Um mod de cliente é ignorado em silêncio em um servidor dedicado.
entrypoints As classes que implementam CraftingMod.
depends, optional, incompatible Id → intervalo de versões: >=1.2 <2, ^1.4, ~1.4.2, 1.20.x, [1.0,2.0). Se algo falhar, o Crafting informa todos os problemas de uma vez, com uma sugestão.
loadAfter, loadBefore Ordem de carregamento sem dependência obrigatória.

Ciclo de vida

dev.crafting.api.mod.CraftingModKotlin
interface CraftingMod {
    fun onLoad(ctx: ModContext)
    fun onReady(ctx: ModContext) {}
    fun onShutdown(ctx: ModContext) {}
}
  1. onLoad: registre conteúdo, eventos e comandos.
  2. O Crafting cria os blocos e itens no jogo. A partir daí, registrar conteúdo lança RegistryFrozenException.
  3. onReady: todos os mods estão carregados; Crafting.runtime.isModLoaded("other_mod") diz se outro mod está presente.
  4. onShutdown: ao sair.

Plugins de servidor

Um plugin é um CraftingMod com "kind": "plugin" e "side": "server"; crafting new chatguard --kind plugin cria a classe de entrada, events/ChatFilter.kt e commands/HelloCommand.kt. Ele não adiciona blocos nem itens, então jogadores com o Minecraft sem mods podem entrar em um servidor que só tem plugins. Você o ativa, desativa e recarrega sem reiniciar: onLoad e onReady rodam quando ele é ativado, onShutdown quando é desativado ou recarregado.

Terminal
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("O ChatGuard está ativo") }
        }
    }

    override fun onShutdown(ctx: ModContext) = ctx.logger.info("ChatGuard desativado")
}
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("&cPor favor, mantenha a gentileza")
            }
        }
    }
}

Tudo o que um plugin registra fica ligado a ele. /crafting reload chatguard chama onShutdown, remove seus listeners, comandos e tarefas, lê o jar de novo e chama onLoad. No modo de desenvolvimento, isso acontece automaticamente quando você substitui o jar.

Mod Plugin
Pasta mods/ plugins/
Lado Cliente e/ou servidor Só servidor
Blocos e itens novos Sim Não
Recarregar sem reiniciar Não Sim
Clientes sem mods podem entrar Não, se adicionar conteúdo Sim

Java

Mesma API, mesma estrutura. Em Java, o ponto de entrada é Crafting.INSTANCE, você passa todos os parâmetros que têm valor padrão, e um listener retorna 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;
        });
    }
}

Jeitos antigos

Ainda funcionam, mas estão marcados como obsoletos e têm um substituto:

Antes Agora
class X : CraftingPlugin() com onEnable/onDisable CraftingMod com "kind": "plugin"
Métodos com @Subscribe e events.register(obj) events.on<E> { } (Java: events.subscribe(E.class, …))
@Mod("id") na classe A classe em entrypoints do manifesto
EntityDamageEvent.source damageType (um id, como minecraft:fall)
world.setBlockState(x, y, z, "…[open=true]") world.setBlock(x, y, z, type, mapOf("open" to true))

Regras

  • Não importe nada de net.minecraft. É isso que permite ao mod continuar funcionando quando o jogo muda.
  • Registre conteúdo só em onLoad.
  • Uma tarefa runAsync não mexe no mundo; volte para a thread do jogo com runSync.