Посібники

CraftScript

Мова Crafting. Ключові слова англійською, короткий синтаксис, жодної компіляції: ти зберігаєш файл, і мод перезавантажується. Вона підходить і для мода на десять рядків, і для мода на сорок файлів із класами й типами.

Що можна зробити вже сьогодні

Сфера Як
Предмети item "ruby" { … }: рідкісність, сяйво, їжа, інструмент, міцність, текстура, вкладка творчого режиму, дія під час використання.
Блоки block "ruby_ore" { … }: міцність, вибухостійкість, світло, звук, інструмент і мінімальний рівень, здобич, тертя, прозорість, дія під час використання. Форми та моделі: дивись Форми блоків. Сходи, двері, паркани, скрині, таблички, посіви, руди та інші види блоків гри, сидіння й блоки заввишки в два: дивись Види блоків. Блоки також можуть реагувати на кроки й тіки, бути порталами, мати стани, використовувати редстоун і зберігати предмети: див. Блоки, предмети й рецепти.
Генерація світу ore, tree, patch, structure, biome: те, що з’являється в нових чанках; terrain, biome_map, carver і структури з plan/build для рельєфу, біомів, печер і споруд, створених кодом. Дивись Генерація світу і Генерація кодом.
Істоти creature "ghost" { … }: тіло ігрової істоти з твоєю текстурою або власною 3D-моделлю, характеристиками, поведінкою, цілями й здобиччю, а також природну появу, приборкання, розведення, їзду, дальні атаки та анімації за ключовими кадрами. Дивись Істоти.
Звуки й частинки sound "chime" { file: … }, particle "spark" { textures: … }; використовуються за id. Дивись Звуки й частинки.
Рецепти recipe shaped, shapeless, smelting. Моделі, здобич, теги й переклади генеруються за тебе.
Події on join, on break(e)… усі події, з пріоритетом і скасуванням.
Броня, зброя, перезарядка armor: { slot, defense, texture }, щоб вдягати, on_hit під час удару, cooldown між використаннями. Див. Блоки, предмети та рецепти.
Ефекти, атрибути, спорядження e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Див. Світ і гравці.
Клавіші key "dash" { label: "Dash", default: "R" } і on key(e); події сутностей on interact_entity і on attack. Див. Інтерфейс і Події.
Команди command pay(to: player, amount: int) requires "eco.pay", з псевдонімами, описом і дозволами.
Час every 5 minutes { }, after 10 seconds { }, on tick.
Світ і гравці Блоки зі станами, сутності, вибухи, частинки, звуки, час доби, скрині, таблички; інвентар, здоров’я, заголовки, телепортація. Дивись Світ і гравці.
Інтерфейс form(…) і player.show_form, server.boss_bar, player.set_sidebar. Дивись Інтерфейс.
Збережені дані p.data.set("rubies.visits", n), p.data.get_int("rubies.visits"): у гравців, сутностей, блоків і світів; переживають перезапуск. Дивись Світ і гравці.
Налаштування й мережа config("settings", { … }), listen, send, send_to_server. Дивись Налаштування й мережа.
Великі моди Необов’язкові типи, класи з успадкуванням, переліки, модулі, константи.

Лише Kotlin: фонова робота, бібліотеки Java і, в генерації світу, фічі та теги, створені кодом.

Файли

Мод — це один файл (mods/gems.craft) або, що краще, коли він розростається, тека з main.craft і файлами, які той імпортує. main.craft починається із заголовка mod, що задає id, назву й версію:

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"

Як розкласти решту по теках і чому: Як організувати мод.

Значення й змінні

lib/values.craftCraftScript
let coins = 100
const MAX_HOMES = 3                          // const: не можна присвоїти знову
let greeting = "У тебе {coins * 2} монет"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }

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

Числа (завжди дробові), рядки, true/false, null, списки й мапи. Подвійні лапки підставляють {expression}; \{ записує звичайну фігурну дужку. Одинарні лапки нічого не підставляють. Кольори чату записуються через &: "&aЗелений".

Керування виконанням

lib/flow.craftCraftScript
fun describe(coins: number, banned: bool): string {
    if coins > 100 and not banned {
        return "багатій"
    } else if coins == 0 {
        return "без грошей"
    }
    return "нормально"
}

fun countdown() {
    for i in 1..5 { broadcast("{6 - i}…") }    // діапазон із кінцями включно
    for p in players() { p.send("Старт!") }
}

fun who(target: Player?, fallback: Player): string {
    let p = target ?? fallback                  // ?? : значення, якщо null
    return p?.name ?? "ніхто"                   // ?. : безпечно з null
}

Функції

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

Параметри зі значеннями за замовчуванням, return і анонімні функції через fun(x) { … } — саме так їх передають у map, filter або on_use.

Події

Усередині on властивості події — це змінні (player, message, block…), або ж можна дати події назву: on chat(e). e.cancel() скасовує її, якщо її можна скасувати.

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("&cБудь ласка, спілкуйся дружньо")
    }
}

Команди

commands/heal.craftCraftScript
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Вилікувати гравця" {
    let p = target ?? player
    if p == null { return "Вкажи гравця" }
    p.health = min(p.max_health, p.health + (amount ?? 20))
    return "{p.name} вилікувано"
}

Усередині команди є sender, player (або null, якщо її виконує консоль) і args. Те, що ти повертаєш через return, і є відповіддю. Якщо аргументу бракує або він не того типу, гравець бачить правильний синтаксис.

Час

events/reminders.craftCraftScript
every 5 minutes { broadcast("&6Не забудь проголосувати") }

after 10 seconds { log("сервер працює вже 10 секунд") }

Великі моди

Необов’язкові типи

Де ти пишеш тип, він перевіряється; де не пишеш, код лишається вільним. Навіть якщо ти не напишеш жодного, перевірка знає типи всього API:

events/welcome.craftCraftScript
on join {
    player.helth = 20          // друкарська помилка
    player.name = "Admin"      // властивість лише для читання
    player.give()              // бракує аргументу
}
crafting checkТекст
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, "багато")  →  error: Argument 'amount' of heal() must be number, got string

Типи: number, string, bool, id, list<T>, map<T>, T? (може бути null), any, void, fun, типи API (Player, World, Block, Entity, ItemStack, Location, Inventory…), події, а також твої класи й переліки. Після перевірки на null перевірка знає, що значення існує:

commands/home.craftCraftScript
command home(target: player?) {
    if target == null { return "Вкажи гравця" }
    target.teleport(world().block_at(0, 80, 0).location)   // тут target уже не null
}

Класи

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)      // VIP платять половину
    }
}

Присвоєння поля, якого клас не оголошує, — помилка (тож друкарські помилки не прослизнуть). Якщо клас визначає init(…), це конструктор; якщо ні, аргументи заповнюють поля по черзі. is(value, Class) перевіряє тип.

Переліки

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

Модулі

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

let wallets = {}

on join {
    wallets[player.name] = VipWallet()
    player.send("Ранг: {rank_for(wallets[player.name].coins).name}")
}

Усе, що файл оголошує поза будь-яким блоком, можна імпортувати: функції, класи, переліки, константи. Кожен файл виконується лише один раз, навіть якщо його імпортують кілька файлів.

Перевірка, перезавантаження й безпека

  • Усе перевіряється під час завантаження мода: помилка не дає йому завантажитися й каже, де вона; попередження йде в журнал. crafting check main.craft робить те саме з термінала, ідучи за імпортами.
  • У режимі розробки (-Dcrafting.dev=true, завжди ввімкнений у crafting sandbox) збереження перезавантажує мод; так само діє /crafting reload <id>. Новий предмет чи блок потребує перезапуску гри; усе інше змінюється одразу.
  • Скрипт бачить лише API Crafting: він не може читати файли, відкривати з’єднання чи використовувати класи Java.
  • Кожна подія, команда чи таймер мають ліміт у 2 000 000 кроків: нескінченний цикл переривається з помилкою, а не заморожує сервер.
  • Помилка під час виконання записується в журнал із файлом і рядком, а сервер працює далі.

Усі функції, оператори й члени: Довідник CraftScript.