Skip to content

Blocks

Lesson 12.2

A block is like an item that lives in the world. You register it the same way, and it needs a few more asset files: one for how it looks as a block and one for how it looks in your hand.

Registering a block

cl.on("lifecycle.register", function()
  cl.blocks.register{
    id = "ruby_block",
    copy_properties_from = "minecraft:emerald_block",
    vanilla_fallback = "minecraft:emerald_block",
    item = "my_mod:ruby_block",
  }
end)
  • copy_properties_from takes hardness, blast resistance, sounds and the right tool from a vanilla block. It is the easiest way to get a block that feels right.
  • vanilla_fallback is what players without your mod see.
  • item also registers an item for the block, so players can hold and place it. Use the block’s own id.

Like items, blocks are registered once at startup in lifecycle.register, and changes need a restart.

How a mod block behaves

Under the hood, a mod block stands on a vanilla block. That base block is vanilla_fallback, or copy_properties_from if there is no fallback, or stone if you set neither. Players without your mod see the base block, and it decides what the properties below don’t cover: the shape, which tool is needed, how it reacts to explosions and pistons.

So pick the base that feels closest to what you want. A glowing crystal could stand on "minecraft:glowstone", a slippery block on "minecraft:ice", a bouncy one on "minecraft:slime_block":

cl.blocks.register{
  id = "glow_crystal",
  vanilla_fallback = "minecraft:glowstone",
  item = "my_mod:glow_crystal",
}

Your block still has its own id, look and name, and your own behaviour code (below).

Your own block properties

These fields change how the block feels, on top of the base:

Field Means Minecraft examples
destroy_time How long it takes to break (hardness) dirt 0.5, stone 1.5, obsidian 50
friction How slippery it is to walk on (higher is more slippery) most blocks 0.6, slime 0.8, ice 0.98
speed_factor Walking speed on it most blocks 1.0, soul sand and honey 0.4
jump_factor Jump height from it most blocks 1.0, honey 0.5
light_emission The light it gives, 0 to 15 torch 14, glowstone 15
sound_type The sounds of breaking, placing and walking on it "glass", "wood", "stone", "amethyst_block", or any block id like "minecraft:glass"
cl.blocks.register{
  id = "frost_crystal",
  vanilla_fallback = "minecraft:packed_ice",
  item = "my_mod:frost_crystal",
  destroy_time = 0.8,
  friction = 0.98,
  light_emission = 7,
  sound_type = "amethyst_block",
}

A field you leave out keeps the value of copy_properties_from, or of the base block. So copy_properties_from = "minecraft:emerald_block" plus light_emission = 10 gives an emerald-like block that glows.

The server uses these values for everything it simulates: mobs and items slide on the block, mining takes destroy_time, the light spreads. Players with your mod predict the same on their side, so walking and mining feel smooth. Players without it walk on the base block on their screen and get corrected by the server now and then, like on a server with lag, and they hear the base block’s sounds.

Drops and loot tables

When a player mines the block with the right tool, it drops its item. In creative mode it drops nothing, like vanilla blocks.

For other drops, put a loot table into your mod’s data pack, at datapack/<mod id>/loot_table/blocks/<block name>.json. It is the same format as Minecraft’s own block loot tables, and it may name your mod’s items:

datapack/my_mod/loot_table/blocks/ruby_ore.json:

{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:alternatives",
          "children": [
            { "type": "minecraft:item", "condition": "minecraft:tool/can_silk_touch", "name": "my_mod:ruby_ore" },
            {
              "type": "minecraft:item",
              "name": "my_mod:ruby",
              "modifier": [
                { "type": "minecraft:set_count", "count": { "type": "minecraft:uniform", "min": 1, "max": 2 } },
                { "type": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }
              ]
            }
          ]
        }
      ]
    }
  ]
}

With silk touch the ore drops itself; otherwise one or two rubies, more with fortune. The table is used whenever the block breaks: by a player, by an explosion, or with /loot. A block with a table drops only what the table says, not its item. The easiest way to start is copying a vanilla table (Minecraft’s data/minecraft/loot_table/blocks/ folder) and changing the item names. Mind the format of this Minecraft version: item changes go into "modifier" and each one has a "type".

The data pack is part of your mod, so it is on the server whether or not players install the mod.

Blocks are simple cubes for now: one look, no properties like “facing” or “lit”, and a full-cube model.

Assets for a block

For my_mod:ruby_block:

assets/my_mod/
  blockstates/ruby_block.json       which model the block uses
  models/block/ruby_block.json      the cube model
  textures/block/ruby_block.png     the texture, 16 x 16
  items/ruby_block.json             the model in hand and in inventories
  lang/en_us.json                   "block.my_mod.ruby_block": "Block of Ruby"

blockstates/ruby_block.json. The block has a single state, written as "":

{
  "variants": {
    "": { "model": "my_mod:block/ruby_block" }
  }
}

models/block/ruby_block.json, a cube with the same texture on all six sides:

{
  "parent": "minecraft:block/cube_all",
  "textures": {
    "all": "my_mod:block/ruby_block"
  }
}

items/ruby_block.json reuses the block model for the item:

{
  "model": {
    "type": "minecraft:model",
    "model": "my_mod:block/ruby_block"
  }
}

Block names use the key block.<mod id>.<name>.

Behaviour

Blocks can have code too. Three kinds of behaviour exist:

Behaviour Runs when Arguments
use_without_item A player right-clicks the block ctx, player, hand
attack A player left-clicks (starts hitting) it ctx, player
random_tick The block gets a random tick ctx

ctx has ctx.level (the world) and ctx.pos (the block’s position).

cl.blocks.register{
  id = "doorbell",
  copy_properties_from = "minecraft:oak_planks",
  item = "my_mod:doorbell",
  behavior = {
    use_without_item = function(ctx, player, hand)
      ctx.level:play(ctx.pos:center(), "minecraft:block.note_block.bell", "blocks", 1.0, 1.0)
      return "success"
    end,
  },
}

Return "success" when your code handled the click. Return nothing to let the game do what the fallback block would do.

random_tick runs now and then, at the same rate crops grow (on average about every 68 seconds per block, with the default randomTickSpeed). It is the right tool for slow changes, like a block that turns into another one over time.

Like item behaviour, the code inside a behaviour reloads when you save.

Try it

Register a my_mod:bounce_pad block that bounces like a slime block and says “Boing!” in the action bar when right-clicked. Add the language entry.

Show a solution
cl.on("lifecycle.register", function()
  cl.blocks.register{
    id = "bounce_pad",
    vanilla_fallback = "minecraft:slime_block",
    item = "my_mod:bounce_pad",
    behavior = {
      use_without_item = function(ctx, player, hand)
        player:send_action_bar("Boing!")
        return "success"
      end,
    },
  }
end)
{
  "block.my_mod.bounce_pad": "Bounce Pad"
}

The slime block as base gives the bounce, the sounds and the quick breaking. Players without the mod see a slime block.

Recap: register blocks with cl.blocks.register{ ... } in lifecycle.register, pick a base block with vanilla_fallback, add item for the block item, give them a blockstate, model, texture and name, and use behavior for clicks and random ticks.