API
Blocks, items and recipes
You declare content as a list of properties. Crafting creates the real game objects at the right time and generates the models, loot tables, tags, recipes and translations they need. You don't write any game data files.
Items
item "ruby" { rarity: "rare" }
item "ruby_apple" {
food: { nutrition: 8, saturation: 1.2, always_edible: true }
glint: true
}
item "ruby_pickaxe" { tool: { type: "pickaxe", tier: "diamond", speed: 9 }, tab: "tools_and_utilities" }| Property | Values | Default |
|---|---|---|
stack or max_stack |
1–99 | 64 |
rarity |
common, uncommon, rare, epic (name color) |
common |
glint |
enchantment glint | false |
durability |
number; forces stack: 1 |
0 |
fireproof |
doesn't burn in lava or fire | false |
food |
number, or { nutrition, saturation, always_edible } |
— |
tool |
"pickaxe", or { type, tier, damage, speed }; type: pickaxe, axe, shovel, hoe, sword |
— |
tab |
creative inventory tab (below) | ingredients |
texture |
texture path | item/<id> |
on_use |
fun(player, stack); returns true if it handles the use |
— |
armor |
{ slot, defense, toughness, knockback_resistance, texture } |
— |
on_hit |
fun(attacker, target, stack) |
— |
cooldown |
seconds | 0 |
Armor, weapons and cooldowns
An item with armor can be worn. slot is head, chest, legs or feet. defense, toughness and knockback_resistance (0 to 1) are the game's armor values: an iron chestplate has 6 defense, diamond has 2 toughness.
item "frost_helmet" { texture: "minecraft:item/iron_helmet", armor: { slot: "head", defense: 3, texture: "frost" } }
item "frost_chestplate" { armor: { slot: "chest", defense: 8, toughness: 2 }, durability: 400 }texture is how it looks worn. frost means textures/entity/equipment/humanoid/frost.png in the mod and, for legs, textures/entity/equipment/humanoid_leggings/frost.png, with the same layout as the game's armor textures. Without it, the armor looks like iron.
on_hit runs when the item is used to hit something, whether by a player or by a mob.
item "frost_blade" {
texture: "minecraft:item/diamond_sword"
tool: "sword"
cooldown: 3
on_hit: fun(attacker, target, stack) {
target.add_effect("minecraft:slowness", 4, 4)
target.world.spawn_particle("minecraft:snowflake", target.location.add(0, 1, 0), 20, 0.4, 0.02)
}
}cooldown (in seconds) blocks the item for that long after each use that its on_use took, and the hotbar shows it. From code: player.set_cooldown("minecraft:feather", 2) and player.has_cooldown(id). In Kotlin: armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") for armor and onHit = { ctx -> … } for weapons (ctx.attacker, ctx.target, ctx.stack).
item("frost_helmet") { armor(EquipmentSlot.HEAD, defense = 3, texture = "frost") }Blocks
block "ruby_ore" {
hardness: 3
tool: "pickaxe"
tier: "iron"
loot: [
{ item: "rubies:ruby", count: [1, 2] },
{ item: "minecraft:diamond", chance: 0.1 }
]
}
block "ruby_block" { hardness: 5, light: 7, tool: "pickaxe", sound: "metal" }
block "ruby_lamp" { hardness: 0.3, light: 15, sound: "glass", transparent: true, tab: "functional_blocks" }
block "ruby_bricks" { hardness: 2, tool: "pickaxe", drops: "ruby", drop_count: [2, 4] }| Property | Values | Default |
|---|---|---|
hardness |
number; -1 = unbreakable |
1.5 |
resistance |
explosion resistance | = hardness |
light |
0–15 | 0 |
light_when |
a toggle state, like "lit": the block shines only while it is on (see Block states) |
always |
sound |
stone, wood, gravel, grass, metal, glass, wool, sand | stone |
like |
a block it is made like, the game's ("minecraft:end_stone") or yours: its hardness, resistance, sound and whether it needs the right tool to drop anything are that block's |
— |
kind |
one of the game's kinds: stairs, door, chest, crop… (see Block kinds) | cube |
tool, tier |
tool required for the block to drop anything, and the minimum material of that tool: wood, stone, iron, diamond, netherite | —, wood |
drops, drop_count |
a single item: id; number or [min, max] |
the block itself, 1 |
loot |
several items: [{ item, count, chance, tool, fortune }, …]. Each one is rolled separately; count is a number or [min, max] and chance goes from 0 to 1 (default 1). tool and fortune: see Drops below |
— |
friction |
0.6 normal, 0.98 ice | 0.6 |
transparent |
you can see through it (glass, leaves) | false |
has_item |
also register the block's item; without one, the block drops nothing unless drops or loot say what |
true |
tab |
creative inventory tab | building_blocks |
on_use |
fun(player, block); returns true if it handles the use. A player who sneaks with something in hand places it instead, as with the game's blocks |
— |
Tabs: building_blocks, colored_blocks, natural_blocks, functional_blocks, redstone_blocks, tools_and_utilities, combat, food_and_drinks, ingredients, spawn_eggs. In Kotlin they are in CreativeTabs.
To make a creature drop something extra when it dies, add it to e.drops in the entity_death event (see Events).
Blocks that aren't cubes, blocks of the game's kinds, creatures, ores, sounds and particles are declared the same way and have their own pages: Block shapes, Block kinds, Creatures, World generation, Sounds and particles.
Drops: tools and fortune
A drop in loot can need a tool, and fortune can give more of it:
block "ruby_ore" {
kind: "ore"
tool: "pickaxe"
tier: "iron"
xp: [1, 4]
loot: [
{ item: "ruby", count: [1, 2], fortune: true },
{ item: "ruby_ore", tool: "silk_touch" }
]
}
block "frost_leaves" {
kind: "leaves"
loot: [
{ item: "frost_leaves", tool: "shears" },
{ item: "frost_sapling", chance: 0.05, fortune: true }
]
}tool: "silk_touch": it drops only when the block is broken with silk touch.tool: "shears": only with shears or silk touch, as the game's leaves and vines drop themselves.- When some drops need a tool, the drops without one happen only when that tool isn't used: the ore above drops itself with silk touch and its rubies otherwise.
fortune: true: fortune gives more of it, more of itscountas with the game's ores, or a betterchanceas with saplings from leaves.
In Kotlin: drop("ruby", 1..2, tool = null, fortune = true) and drop("ruby_ore", tool = DropTool.SILK_TOUCH).
block("ruby_ore") {
kind = BlockKind.ORE
xp = 1..4
drop("ruby", 1..2, tool = null, fortune = true)
drop("ruby_ore", tool = DropTool.SILK_TOUCH)
}Recipes
recipe shaped "ruby_block" {
pattern: ["RRR", "RRR", "RRR"]
keys: { R: "ruby" }
result: "ruby_block"
}
recipe shapeless "rubies_from_block" { ingredients: ["ruby_block"], result: "ruby", count: 9 }
recipe smelting "ruby_from_ore" { input: "ruby_ore", result: "ruby", xp: 1.0 }Use behavior
on_use is the only logic that lives next to the data. If it grows beyond a couple of lines, write the function in lib/ and import it:
import { lift } from "/lib/teleport"
item "lift_wand" { stack: 1, rarity: "uncommon", on_use: lift }fun lift(player, stack) {
player.teleport(player.location.add(0, 10, 0))
return true
}Blocks that react
A block can run code when something happens to it or around it. Each handler receives the block and, where it applies, the entity.
| Property | Runs when |
|---|---|
on_step: fun(block, entity) |
an entity walks on the block. |
on_inside: fun(block, entity) |
an entity is inside the block. The block has to let entities through: a portal, a plant or a non-solid shape. |
on_random_tick: fun(block) |
now and then, at random, like crops growing: about once every 68 seconds per block. |
on_tick: fun(block) |
the time asked for with block.schedule(ticks) has passed (20 ticks = 1 second). |
on_neighbor_change: fun(block) |
a block next to it changed: it was placed, broken or powered. |
on_placed: fun(block, entity) |
someone placed the block; entity is who. |
block "jump_pad" { on_step: fun(b, e) { e.velocity = vector(0, 1.1, 0) } }
block "blinker" {
light: 7
on_tick: fun(b) {
b.world.spawn_particle("minecraft:end_rod", b.location.add(0.5, 1.2, 0.5), 4, 0.1, 0.01)
b.schedule(20)
}
on_neighbor_change: fun(b) { b.schedule(1) }
}In Kotlin the handlers are onStep, onInside, onRandomTick, onTick, onNeighborChange and onPlaced. Each receives a BlockEventContext with block and, when there is one, entity:
block("pad") { onStep = { ctx -> ctx.entity?.velocity = Vector(0.0, 1.1, 0.0) } }Portals
A block with portal takes whoever is inside it to another dimension. Give the dimension to go to, or to and back to choose where it leads from there:
block "sky_gate" { portal: "gatemod:sky", light: 11 }
block "sky_gate_two_way" { portal: { to: "gatemod:sky", back: "minecraft:overworld" } }The entity arrives on the ground at the same x and z and finds a portal block there to come back by; if there is none nearby, one is placed beside the arrival. Entities wait a moment before using a portal again. Portal blocks let entities through. Declare the dimension itself with dimension (see @dim@). In Kotlin:
block("sky_gate") { portal("gatemod:sky") }Block states
states gives a block values it remembers and can show:
block "lamp" {
model: "block/lamp"
states: ["facing", "lit"]
on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
on_neighbor_change: fun(b) { b.set_state("lit", b.redstone_power > 0) }
}
block "crop" { states: [{ stage: 3 }, { wet: true }] }"facing": north, east, south or west. It is set facing the player who places the block, and the model turns with it."lit": a name alone is an on/off toggle, off by default.{ lit: true }makes it on by default.{ stage: 3 }: a number from 0 to the maximum you give (1 to 15)."facing_all": like"facing", but also up and down. It is set facing the player who places it, and the model is tipped over for up and down.{ shape: ["top", "middle", "bottom"] }: one of a few names, the first by default. Names are lowercase letters, digits and_.
Read and change them with block.state("lit") and block.set_state("lit", true). They work on the game's own blocks too, for example a furnace's facing and lit.
A state that is off its default can look different. Add a texture at textures/block/<id>_<name>.png (a toggle that is on) or <id>_<name><n>.png (a number) or <id>_<value>.png (a name), or a model at models/block/<id>_<name>.json. The whole combination (<id>_lit_stage2) is tried first, then each state alone. In the example, lamp.json and lamp_lit.json are the mod's two models, for instance with the parent minecraft:block/orientable and front, side and top textures.
Light by state. light_when: "lit" makes the block's light shine only while that toggle is on, as in a lamp:
block "lamp" {
light: 15
light_when: "lit"
states: ["lit"]
on_use: fun(p, b) { b.set_state("lit", !b.state("lit")); return true }
}In Kotlin: facing(), facingAll(), toggle("lit"), number("stage", 3), choice("shape", "top", "middle", "bottom"), and block.state(…) and block.setState(…) in code:
block("lamp") {
model = "block/lamp"
facing()
toggle("lit")
number("stage", 3)
}Effects
A mod can add its own status effects. They appear on the player's screen like the game's and are given and read the same way.
effect "frozen" {
color: "#88CCFF"
kind: "harmful"
on_tick: fun(e, level) { e.damage(level) }
}color: the color of its particles and its icon background.kind:beneficial,harmfulorneutral.on_tick:fun(e, level), runs every second while an entity has the effect, with its level (1 is I).- The icon is the mod's
textures/mob_effect/<id>.png.
Give it with e.add_effect("mymod:frozen", 3, 1) (id, seconds, level) and check it with e.has_effect("mymod:frozen"), as with the game's effects (see The world and players). In Kotlin:
content.effect("frozen") {
color = 0x88CCFF
kind = EffectKind.HARMFUL
onTick = { e, level -> /* … */ }
}Redstone
block "battery" {
texture: "minecraft:block/copper_block"
redstone: true
on_use: fun(p, b) { if b.state("power") > 0 { b.set_state("power", 0) } else { b.set_state("power", 15) }; return true }
}redstone: true makes the block give off a signal as strong as its power state, a number from 0 to 15 that is added automatically. b.set_state("power", 15) turns it on, and the wires, lamps and doors around it follow. block.redstone_power is the power the block receives from around it, and on_neighbor_change runs when that changes (the lamp above uses it).
In Kotlin: redstone = true and block.redstonePower.
block("battery") {
texture = "minecraft:block/copper_block"
redstone = true
// in a handler: ctx.block.redstonePower
}Inventories
block "crate" { texture: "minecraft:block/barrel_side", inventory: 27 }inventory: 27 gives the block that many slots (9 to 54, in rows of 9). It keeps its items across restarts, using it opens a chest screen (unless its on_use took the click), and breaking it drops what it holds. From 1 to 8 slots there is no screen: the mod puts items in and takes them out itself.
From code, block.inventory (it also works for the game's chests and barrels) offers set(slot, item(…)), get(slot), count(id) and add(…). In Kotlin: inventory = 27.
block("crate") {
texture = "minecraft:block/barrel_side"
inventory = 27
}Items shown on a block. shows_item: 14 makes the item in the block's first slot float above it, turning, that many pixels above its bottom. Every player sees it, and it goes when the slot empties or the block is broken. A pedestal takes the item in the player's hand and gives it back:
block "pedestal" {
shape: [[2, 0, 2, 14, 12, 14]]
inventory: 1
shows_item: 14
on_use: fun(p, b) {
let inv = b.inventory
if inv.get(0) == null and p.held_item != null {
inv.set(0, p.held_item)
p.set_held_item(null)
} else if inv.get(0) != null {
p.give(inv.get(0))
inv.set(0, null)
}
return true
}
}player.set_held_item(stack) puts an item in the player's main hand, or empties it with null. In Kotlin: showsItem = 14 and player.setHeldItem(stack).
block("pedestal") {
box(2, 0, 2, 14, 12, 14)
inventory = 1
showsItem = 14
onUse { ctx ->
val inv = ctx.block.inventory!!
val held = ctx.player.heldItem
val shown = inv[0]
if (shown == null && held != null) { inv[0] = held; ctx.player.setHeldItem(null) }
else if (shown != null) { ctx.player.give(shown); inv[0] = null }
true
}
}Textures
Textures ship inside the mod; nobody has to install a resource pack. The file name is the id:
| Mod | Where |
|---|---|
| CraftScript (folder) | mods/rubies/textures/item/ruby.png, mods/rubies/textures/block/ruby_ore.png |
| Kotlin or Java | src/main/resources/assets/rubies/textures/item/ruby.png |
If a mod ships its own models, recipes or loot tables in those same folders, Crafting uses them.
In Kotlin
package com.example.rubies.content
object Blocks {
fun register(content: ContentBuilder) {
content.block("ruby_ore") {
hardness = 3f
requiresTool = ToolType.PICKAXE
minTier = ToolTier.IRON
drop("ruby", 1..2)
drop("minecraft:diamond", chance = 0.1)
}
content.block("ruby_block") { hardness = 5f; luminance = 7; sound = SoundGroup.METAL }
}
}Ids. Without a namespace, an id belongs to your mod ("ruby" is rubies:ruby). For game content, write the namespace: "minecraft:diamond". You can't register content in the minecraft namespace.