Guide

CraftScript

Il linguaggio di Crafting. Parole chiave in inglese, sintassi breve, nessuna compilazione: salvi il file e la mod si ricarica. Va bene per una mod di dieci righe e per una di quaranta file con classi e tipi.

Cosa puoi fare oggi

Ambito Come
Oggetti item "ruby" { … }: rarità, luccichio, cibo, strumento, durabilità, texture, scheda della modalità Creativa, azione d’uso.
Blocchi block "ruby_ore" { … }: durezza, resistenza, luce, suono, strumento e livello minimo, drop, attrito, trasparenza, azione d’uso. Forme e modelli: vedi Forme dei blocchi. Scale, porte, recinti, bauli, cartelli, coltivazioni, minerali e gli altri tipi del gioco, sedute e blocchi alti due: vedi Tipi di blocco. I blocchi possono anche reagire ai passi e ai tick, essere portali, avere stati, usare la redstone e contenere oggetti: vedi Blocchi, oggetti e ricette.
Generazione del mondo ore, tree, patch, structure, biome: ciò che compare nei nuovi chunk; terrain, biome_map, carver e strutture con plan/build per terreno, biomi, grotte ed edifici creati dal codice. Vedi Generazione del mondo e Generazione tramite codice.
Creature creature "ghost" { … }: il corpo di una creatura del gioco con la tua texture o il tuo modello 3D, statistiche, comportamento, bersagli e bottino, più comparsa naturale, addomesticamento, allevamento, cavalcata, attacchi a distanza e animazioni a fotogrammi chiave. Vedi Creature.
Suoni e particelle sound "chime" { file: … }, particle "spark" { textures: … }; si usano tramite id. Vedi Suoni e particelle.
Ricette recipe shaped, shapeless, smelting. Modelli, bottino, tag e traduzioni vengono generati per te.
Eventi on join, on break(e)… tutti gli eventi, con priorità e annullamento.
Armature, armi, tempi di ricarica armor: { slot, defense, texture } per indossarlo, on_hit quando colpisce, cooldown tra un uso e l'altro. Vedi Blocchi, oggetti e ricette.
Effetti, attributi, equipaggiamento e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Vedi Il mondo e i giocatori.
Tasti key "dash" { label: "Dash", default: "R" } e on key(e); eventi di entità on interact_entity e on attack. Vedi Interfaccia ed Eventi.
Comandi command pay(to: player, amount: int) requires "eco.pay", con alias, descrizione e permessi.
Tempo every 5 minutes { }, after 10 seconds { }, on tick.
Mondo e giocatori Blocchi con stati, entità, esplosioni, particelle, suoni, ora del giorno, casse, cartelli; inventario, salute, titoli, teletrasporto. Vedi Il mondo e i giocatori.
Interfaccia form(…) e player.show_form, server.boss_bar, player.set_sidebar. Vedi Interfaccia.
Dati salvati p.data.set("rubies.visits", n), p.data.get_int("rubies.visits"): su giocatori, entità, blocchi e mondi; sopravvivono a un riavvio. Vedi Il mondo e i giocatori.
Impostazioni e rete config("settings", { … }), listen, send, send_to_server. Vedi Configurazione e rete.
Mod grandi Tipi facoltativi, classi con ereditarietà, enum, moduli, costanti.

Solo Kotlin: lavoro in background, librerie Java e, nella generazione del mondo, feature e tag creati dal codice.

File

Una mod è un singolo file (mods/gems.craft) oppure, meglio quando cresce, una cartella con main.craft e i file che importa. main.craft inizia con l’intestazione mod, che indica id, nome e versione:

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"

Come dividere il resto in cartelle, e perché: Organizzare una mod.

Valori e variabili

lib/values.craftCraftScript
let coins = 100
const MAX_HOMES = 3                          // const: non si può riassegnare
let greeting = "Hai {coins * 2} monete"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }

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

Numeri (sempre decimali), stringhe, true/false, null, liste e mappe. Le virgolette doppie interpolano {expression}; \{ scrive una graffa letterale. Le virgolette singole non interpolano. I colori della chat si scrivono con &: "&aVerde".

Controllo del flusso

lib/flow.craftCraftScript
fun describe(coins: number, banned: bool): string {
    if coins > 100 and not banned {
        return "ricco"
    } else if coins == 0 {
        return "al verde"
    }
    return "a posto"
}

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

fun who(target: Player?, fallback: Player): string {
    let p = target ?? fallback                  // ?? : valore se null
    return p?.name ?? "nessuno"                 // ?. : sicuro su null
}

Funzioni

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

Parametri con valori predefiniti, return e funzioni anonime con fun(x) { … }, che è il modo per passarle a map, filter o on_use.

Eventi

Dentro on, le proprietà dell’evento sono variabili (player, message, block…), oppure puoi dare un nome all’evento: on chat(e). e.cancel() lo annulla se è annullabile.

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("&cResta gentile, per favore")
    }
}

Comandi

commands/heal.craftCraftScript
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Cura un giocatore" {
    let p = target ?? player
    if p == null { return "Indica un giocatore" }
    p.health = min(p.max_health, p.health + (amount ?? 20))
    return "Hai curato {p.name}"
}

Dentro un comando hai sender, player (o null se lo esegue la console) e args. Ciò che restituisci con return è la risposta. Se un argomento manca o ha il tipo sbagliato, il giocatore vede l’uso corretto.

Tempo

events/reminders.craftCraftScript
every 5 minutes { broadcast("&6Ricordati di votare") }

after 10 seconds { log("il server è attivo da 10 secondi") }

Mod grandi

Tipi facoltativi

Dove scrivi un tipo, viene imposto; dove non lo scrivi, il codice resta libero. Anche se non ne scrivi nessuno, il controllo conosce i tipi di tutta l’API:

events/welcome.craftCraftScript
on join {
    player.helth = 20          // errore di battitura
    player.name = "Admin"      // proprietà di sola lettura
    player.give()              // argomento mancante
}
crafting checkTesto
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, "tanto")  →  error: Argument 'amount' of heal() must be number, got string

Tipi: number, string, bool, id, list<T>, map<T>, T? (può essere null), any, void, fun, i tipi dell’API (Player, World, Block, Entity, ItemStack, Location, Inventory…), gli eventi e le tue classi ed enum. Dopo un controllo su null, il controllo sa che il valore esiste:

commands/home.craftCraftScript
command home(target: player?) {
    if target == null { return "Indica un giocatore" }
    target.teleport(world().block_at(0, 80, 0).location)   // qui target non è più null
}

Classi

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)      // i VIP pagano la metà
    }
}

Assegnare un campo che la classe non dichiara è un errore (così gli errori di battitura non passano inosservati). Se la classe definisce init(…), quello è il costruttore; altrimenti gli argomenti riempiono i campi in ordine. is(value, Class) controlla il tipo.

Enum

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

Moduli

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

let wallets = {}

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

Tutto ciò che un file dichiara fuori da qualsiasi blocco è importabile: funzioni, classi, enum, costanti. Ogni file viene eseguito una sola volta anche se lo importano più file.

Controllo, ricarica e sicurezza

  • Tutto viene controllato quando la mod si carica: un errore ne impedisce il caricamento e dice dove; un avviso finisce nel log. crafting check main.craft fa lo stesso dal terminale, seguendo gli import.
  • In modalità sviluppo (-Dcrafting.dev=true, sempre attiva in crafting sandbox) salvare ricarica la mod; lo stesso fa /crafting reload <id>. Un nuovo oggetto o blocco richiede il riavvio del gioco; tutto il resto cambia subito.
  • Uno script vede solo l’API di Crafting: non può leggere file, aprire connessioni o usare classi Java.
  • Ogni evento, comando o timer ha un limite di 2.000.000 di passi: un ciclo infinito viene interrotto con un errore invece di bloccare il server.
  • Un errore in esecuzione viene registrato nel log con file e riga, e il server continua a funzionare.

Ogni funzione, operatore e membro: Riferimento CraftScript.