Guias

CraftScript

A linguagem do Crafting. Palavras-chave em inglês, sintaxe curta, sem compilar: você salva o arquivo e o mod é recarregado. Serve para um mod de dez linhas e para um de quarenta arquivos com classes e tipos.

O que dá para fazer hoje

Área Como
Itens item "ruby" { … }: raridade, brilho, comida, ferramenta, durabilidade, textura, aba do criativo, ação ao usar.
Blocos block "ruby_ore" { … }: dureza, resistência, luz, som, ferramenta e nível mínimo, drops, atrito, transparência, ação ao usar. Formas e modelos: veja Formas de blocos. Escadas, portas, cercas, baús, placas, plantações, minérios e os outros tipos do jogo, assentos e blocos de duas alturas: veja Tipos de bloco. Os blocos também podem reagir a passos e ticks, ser portais, ter estados, usar redstone e guardar itens: veja Blocos, itens e receitas.
Geração de mundo ore, tree, patch, structure, biome: o que aparece nos chunks novos; terrain, biome_map, carver e estruturas com plan/build para terreno, biomas, cavernas e construções feitos por código. Veja Geração de mundo e Geração por código.
Criaturas creature "ghost" { … }: o corpo de uma criatura do jogo com sua textura ou seu próprio modelo 3D, atributos, comportamento, alvos e loot, além de surgimento natural, domesticação, criação, montaria, ataques à distância e animações por quadros-chave. Veja Criaturas.
Sons e partículas sound "chime" { file: … }, particle "spark" { textures: … }; usados pelo id. Veja Sons e partículas.
Receitas recipe shaped, shapeless, smelting. Modelos, loot, tags e traduções são gerados para você.
Eventos on join, on break(e)… todos os eventos, com prioridade e cancelamento.
Armadura, armas, tempos de recarga armor: { slot, defense, texture } para vestir, on_hit ao golpear, cooldown entre usos. Veja Blocos, itens e receitas.
Efeitos, atributos, equipamento e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Veja O mundo e os jogadores.
Teclas key "dash" { label: "Dash", default: "R" } e on key(e); eventos de entidade on interact_entity e on attack. Veja Interface e Eventos.
Comandos command pay(to: player, amount: int) requires "eco.pay", com aliases, descrição e permissões.
Tempo every 5 minutes { }, after 10 seconds { }, on tick.
Mundo e jogadores Blocos com estados, entidades, explosões, partículas, sons, hora do dia, baús, placas; inventário, vida, títulos, teletransporte. Veja O mundo e os jogadores.
Interface form(…) e player.show_form, server.boss_bar, player.set_sidebar. Veja Interface.
Dados salvos p.data.set("rubies.visits", n), p.data.get_int("rubies.visits"): em jogadores, entidades, blocos e mundos; sobrevivem a uma reinicialização. Veja O mundo e os jogadores.
Ajustes e rede config("settings", { … }), listen, send, send_to_server. Veja Configuração e rede.
Mods grandes Tipos opcionais, classes com herança, enums, módulos, constantes.

Só em Kotlin: trabalho em segundo plano, bibliotecas Java e, na geração de mundo, features e tags feitas por código.

Arquivos

Um mod é um único arquivo (mods/gems.craft) ou, melhor quando ele cresce, uma pasta com main.craft e os arquivos que ele importa. main.craft começa com o cabeçalho mod, que dá o id, o nome e a versão:

mods/rubies/main.craftCraftScript
mod "rubies" { name: "Rubies", version: "1.0.0", author: "you" }

// What exists
import "content/items"
import "content/ores"
import "content/world"
import "content/dimensions"
import "content/creatures"
import "content/sounds"
import "content/particles"
import "content/recipes"

// What happens
import "events/mining"
import "events/hunting"
import "events/welcome"
import "commands/realm"
import "commands/rubies"
import "commands/shop"

Como dividir o resto em pastas, e por quê: Organizando um mod.

Valores e variáveis

lib/values.craftCraftScript
let coins = 100
const MAX_HOMES = 3                          // const: não pode ser reatribuída
let greeting = "Você tem {coins * 2} moedas"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }

names.add("Cleo")
prices["diamond"] = 9
log(len(names), prices.gold, prices.keys())

Números (sempre decimais), strings, true/false, null, listas e mapas. Aspas duplas interpolam {expressão}; \{ escreve uma chave literal. Aspas simples não interpolam. As cores do chat são escritas com &: "&aVerde".

Controle de fluxo

lib/flow.craftCraftScript
fun describe(coins: number, banned: bool): string {
    if coins > 100 and not banned {
        return "rico"
    } else if coins == 0 {
        return "sem dinheiro"
    }
    return "ok"
}

fun countdown() {
    for i in 1..5 { broadcast("{6 - i}…") }    // intervalo inclusivo
    for p in players() { p.send("Já!") }
}

fun who(target: Player?, fallback: Player): string {
    let p = target ?? fallback                  // ?? : valor se for null
    return p?.name ?? "ninguém"                 // ?. : seguro com null
}

Funções

lib/rewards.craftCraftScript
fun give_rubies(p: Player, amount: number = 1) {
    p.give(item("rubies:ruby", amount))
}

fun healthy(): list<Player> {
    return players().filter(fun(p) { return p.health > 10 })
}

Parâmetros com valores padrão, return e funções anônimas com fun(x) { … }, que é como você as passa para map, filter ou on_use.

Eventos

Dentro de on, as propriedades do evento são variáveis (player, message, block…), ou você pode dar um nome ao evento: on chat(e). e.cancel() o cancela, se for cancelável.

events/chat.craftCraftScript
const BLOCKED = ["spam", "scam"]

on chat(e) priority high {
    if BLOCKED.any(fun(w) { return e.message.contains(w) }) {
        e.cancel()
        e.player.send("&cPor favor, mantenha a gentileza")
    }
}

Comandos

commands/heal.craftCraftScript
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Cura um jogador" {
    let p = target ?? player
    if p == null { return "Informe um jogador" }
    p.health = min(p.max_health, p.health + (amount ?? 20))
    return "{p.name} foi curado"
}

Dentro de um comando você tem sender, player (ou null se o console o executar) e args. O que você der em return é a resposta. Se faltar um argumento ou ele tiver o tipo errado, o jogador vê o uso correto.

Tempo

events/reminders.craftCraftScript
every 5 minutes { broadcast("&6Lembre-se de votar") }

after 10 seconds { log("o servidor está ligado há 10 segundos") }

Mods grandes

Tipos opcionais

Onde você escreve um tipo, ele é exigido; onde não escreve, o código fica livre. Mesmo que você não escreva nenhum, o verificador conhece os tipos de toda a API:

events/welcome.craftCraftScript
on join {
    player.helth = 20          // erro de digitação
    player.name = "Admin"      // propriedade somente leitura
    player.give()              // falta um argumento
}
crafting checkTexto
welcome.craft:2: error: Player has no property 'helth'. Did you mean 'health'?
welcome.craft:3: error: Player.name is read-only
welcome.craft:4: error: give() expects 1 argument(s), got 0
lib/heal.craftCraftScript
fun heal(p: Player, amount: number = 20): bool {
    p.health = min(p.max_health, p.health + amount)
    return true
}

// heal(player, "muito")  →  error: Argument 'amount' of heal() must be number, got string

Tipos: number, string, bool, id, list<T>, map<T>, T? (pode ser null), any, void, fun, os tipos da API (Player, World, Block, Entity, ItemStack, Location, Inventory…), eventos, e suas próprias classes e enums. Depois de uma verificação de null, o verificador sabe que o valor existe:

commands/home.craftCraftScript
command home(target: player?) {
    if target == null { return "Informe um jogador" }
    target.teleport(world().block_at(0, 80, 0).location)   // aqui target já não é null
}

Classes

lib/wallet.craftCraftScript
class Wallet {
    coins: number = 100

    fun pay(n: number): bool {
        if n > this.coins { return false }
        this.coins -= n
        return true
    }
}

class VipWallet extends Wallet {
    fun pay(n: number): bool {
        return super.pay(n / 2)      // VIPs pagam metade
    }
}

Atribuir um campo que a classe não declara é um erro (assim erros de digitação não passam). Se a classe define init(…), esse é o construtor; se não, os argumentos preenchem os campos em ordem. is(valor, Classe) verifica o tipo.

Enums

lib/rank.craftCraftScript
enum Rank { PEASANT, KNIGHT, LORD, KING }

fun rank_for(coins: number): Rank {
    if coins >= 1000 { return Rank.LORD }
    return Rank.PEASANT
}

// Rank.LORD.name → "LORD", Rank.values() → [PEASANT, …], Rank.from("king") → KING

Módulos

events/economy.craftCraftScript
import { VipWallet } from "/lib/wallet"
import { rank_for } from "/lib/rank"

let wallets = {}

on join {
    wallets[player.name] = VipWallet()
    player.send("Posto: {rank_for(wallets[player.name].coins).name}")
}

Tudo o que um arquivo declara fora de qualquer bloco pode ser importado: funções, classes, enums, constantes. Cada arquivo roda uma única vez, mesmo que vários arquivos o importem.

Verificação, recarga e segurança

  • Tudo é verificado quando o mod carrega: um erro impede o carregamento e diz onde está; um aviso vai para o log. crafting check main.craft faz o mesmo no terminal, seguindo os imports.
  • No modo de desenvolvimento (-Dcrafting.dev=true, sempre ativo em crafting sandbox), salvar recarrega o mod; /crafting reload <id> também. Um item ou bloco novo precisa reiniciar o jogo; todo o resto muda na hora.
  • Um script só vê a API do Crafting: não pode ler arquivos, abrir conexões nem usar classes Java.
  • Cada evento, comando ou timer tem um limite de 2.000.000 passos: um loop infinito é interrompido com um erro em vez de travar o servidor.
  • Um erro em tempo de execução é registrado no log com o arquivo e a linha, e o servidor continua rodando.

Todas as funções, operadores e membros: Referência do CraftScript.