Guides

CraftScript

Le langage de Crafting. Des mots-clés en anglais, une syntaxe courte, pas de compilation : tu enregistres le fichier et le mod se recharge. Il convient à un mod de dix lignes comme à un mod de quarante fichiers avec des classes et des types.

Ce que tu peux faire aujourd’hui

Domaine Comment
Objets item "ruby" { … } : rareté, reflet d’enchantement, nourriture, outil, durabilité, texture, onglet du mode Créatif, action d’utilisation.
Blocs block "ruby_ore" { … } : dureté, résistance, lumière, son, outil et niveau minimum, butin, friction, transparence, action d’utilisation. Formes et modèles : voir Formes de blocs. Escaliers, portes, clôtures, coffres, panneaux, cultures, minerais et les autres sortes de blocs du jeu, sièges et blocs sur deux hauteurs : voir Sortes de blocs. Les blocs peuvent aussi réagir aux pas et aux ticks, être des portails, avoir des états, utiliser la redstone et contenir des objets : voir Blocs, objets et recettes.
Génération du monde ore, tree, patch, structure, biome : ce qui apparaît dans les nouveaux chunks ; terrain, biome_map, carver et des structures avec plan/build pour le relief, les biomes, les grottes et les bâtiments faits par code. Voir Génération du monde et Génération par code.
Créatures creature "ghost" { … } : le corps d’une créature du jeu avec ta texture ou ton propre modèle 3D, ses statistiques, son comportement, ses cibles et son butin, plus apparition naturelle, apprivoisement, élevage, monte, attaques à distance et animations par images clés. Voir Créatures.
Sons et particules sound "chime" { file: … }, particle "spark" { textures: … } ; utilisés par leur id. Voir Sons et particules.
Recettes recipe shaped, shapeless, smelting. Les modèles, le butin, les tags et les traductions sont générés pour toi.
Événements on join, on break(e)… tous les événements, avec priorité et annulation.
Armures, armes, temps de recharge armor: { slot, defense, texture } pour le porter, on_hit quand il frappe, cooldown entre deux usages. Voir Blocs, objets et recettes.
Effets, attributs, équipement e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Voir Le monde et les joueurs.
Touches key "dash" { label: "Dash", default: "R" } et on key(e) ; événements d'entité on interact_entity et on attack. Voir Interface et Événements.
Commandes command pay(to: player, amount: int) requires "eco.pay", avec alias, description et permissions.
Temps every 5 minutes { }, after 10 seconds { }, on tick.
Monde et joueurs Blocs avec états, entités, explosions, particules, sons, heure de la journée, coffres, panneaux ; inventaire, santé, titres, téléportation. Voir Le monde et les joueurs.
Interface form(…) et player.show_form, server.boss_bar, player.set_sidebar. Voir Interface.
Données enregistrées p.data.set("rubies.visits", n), p.data.get_int("rubies.visits") : sur les joueurs, les entités, les blocs et les mondes ; elles survivent à un redémarrage. Voir Le monde et les joueurs.
Réglages et réseau config("settings", { … }), listen, send, send_to_server. Voir Configuration et réseau.
Gros mods Types facultatifs, classes avec héritage, énumérations, modules, constantes.

Kotlin uniquement : le travail en arrière-plan, les bibliothèques Java et, pour la génération du monde, les features et les tags faits par code.

Fichiers

Un mod est un seul fichier (mods/gems.craft) ou, mieux quand il grandit, un dossier avec main.craft et les fichiers qu’il importe. main.craft commence par l’en-tête mod, qui donne l’id, le nom et la version :

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"

Comment répartir le reste en dossiers, et pourquoi : Organiser un mod.

Valeurs et variables

lib/values.craftCraftScript
let coins = 100
const MAX_HOMES = 3                          // const : ne peut pas être réaffectée
let greeting = "Tu as {coins * 2} pièces"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }

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

Des nombres (toujours décimaux), des chaînes, true/false, null, des listes et des maps. Les guillemets doubles interpolent {expression} ; \{ écrit une accolade littérale. Les guillemets simples n’interpolent pas. Les couleurs du chat s’écrivent avec & : "&aVert".

Structures de contrôle

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

fun countdown() {
    for i in 1..5 { broadcast("{6 - i}…") }    // intervalle inclusif
    for p in players() { p.send("C’est parti !") }
}

fun who(target: Player?, fallback: Player): string {
    let p = target ?? fallback                  // ?? : valeur si null
    return p?.name ?? "personne"                // ?. : sûr si null
}

Fonctions

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

Des paramètres avec valeurs par défaut, return, et des fonctions anonymes avec fun(x) { … }, qui permettent de les passer à map, filter ou on_use.

Événements

Dans un on, les propriétés de l’événement sont des variables (player, message, block…), ou tu peux nommer l’événement : on chat(e). e.cancel() l’annule s’il est annulable.

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("&cReste aimable, s’il te plaît")
    }
}

Commandes

commands/heal.craftCraftScript
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Soigne un joueur" {
    let p = target ?? player
    if p == null { return "Indique un joueur" }
    p.health = min(p.max_health, p.health + (amount ?? 20))
    return "{p.name} a été soigné"
}

Dans une commande, tu as sender, player (ou null si c’est la console qui l’exécute) et args. Ce que tu renvoies avec return est la réponse. Si un argument manque ou n’a pas le bon type, le joueur voit l’utilisation correcte.

Temps

events/reminders.craftCraftScript
every 5 minutes { broadcast("&6N’oublie pas de voter") }

after 10 seconds { log("le serveur tourne depuis 10 secondes") }

Gros mods

Types facultatifs

Là où tu écris un type, il est imposé ; là où tu n’en écris pas, le code reste libre. Même si tu n’en écris aucun, le vérificateur connaît les types de toute l’API :

events/welcome.craftCraftScript
on join {
    player.helth = 20          // faute de frappe
    player.name = "Admin"      // propriété en lecture seule
    player.give()              // argument manquant
}
crafting checkTexte
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, "beaucoup")  →  error: Argument 'amount' of heal() must be number, got string

Types : number, string, bool, id, list<T>, map<T>, T? (peut être null), any, void, fun, les types de l’API (Player, World, Block, Entity, ItemStack, Location, Inventory…), les événements, et tes propres classes et énumérations. Après une vérification de null, le vérificateur sait que la valeur existe :

commands/home.craftCraftScript
command home(target: player?) {
    if target == null { return "Indique un joueur" }
    target.teleport(world().block_at(0, 80, 0).location)   // ici, target n’est plus 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)      // les VIP paient moitié prix
    }
}

Affecter un champ que la classe ne déclare pas est une erreur (les fautes de frappe ne passent donc pas). Si la classe définit init(…), c’est le constructeur ; sinon, les arguments remplissent les champs dans l’ordre. is(value, Class) vérifie le type.

Énumérations

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

Modules

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

let wallets = {}

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

Tout ce qu’un fichier déclare en dehors de tout bloc est importable : fonctions, classes, énumérations, constantes. Chaque fichier ne s’exécute qu’une fois, même si plusieurs fichiers l’importent.

Vérification, rechargement et sécurité

  • Tout est vérifié au chargement du mod : une erreur l’empêche de se charger et dit où ; un avertissement va dans le journal. crafting check main.craft fait la même chose depuis le terminal, en suivant les imports.
  • En mode développement (-Dcrafting.dev=true, toujours actif dans crafting sandbox), enregistrer recharge le mod ; /crafting reload <id> aussi. Un nouvel objet ou bloc demande de redémarrer le jeu ; tout le reste change immédiatement.
  • Un script ne voit que l’API de Crafting : il ne peut pas lire de fichiers, ouvrir de connexions ni utiliser de classes Java.
  • Chaque événement, commande ou minuteur a une limite de 2 000 000 étapes : une boucle infinie est interrompue avec une erreur au lieu de geler le serveur.
  • Une erreur à l’exécution est consignée avec le fichier et la ligne, et le serveur continue de tourner.

Chaque fonction, opérateur et membre : Référence CraftScript.