Choose a language en

Node and Item Callbacks

Introduction

Minetest heavily uses a callback-based modding design. A callback is a function that you give to an API and is called when an event happens. For example, you can provide an on_punch function in a node definition to be called when a player punches a node. There are also global callbacks like core.register_on_punchnode to receive events for all nodes.

Item Callbacks

When a player has a node, craftitem, or tool in their inventory, they may trigger certain events:

Callback Default binding Default value
on_use left-click nil
on_place right-click on a node core.item_place
on_secondary_use right-click not on a node core.item_secondary_use (does nothing)
on_drop Q core.item_drop
after_use digging a node nil

on_use

Having a use callback prevents the item from being used to dig nodes. One common use of the use callback is for food:

core.register_craftitem("mymod:mudpie", {
    description = "Alien Mud Pie",
    inventory_image = "myfood_mudpie.png",
    on_use = core.item_eat(20),
})

The number supplied to the core.item_eat function is the number of hit points healed when this food is consumed. Each heart icon the player has is worth two hitpoints. A player can usually have up to 10 hearts, which is equal to 20 hitpoints.

core.item_eat() is a function that returns a function, setting it as the on_use callback. This means the code above is equivalent to this:

core.register_craftitem("mymod:mudpie", {
    description = "Alien Mud Pie",
    inventory_image = "myfood_mudpie.png",
    on_use = function(...)
        return core.do_item_eat(20, nil, ...)
    end,
})

By understanding how item_eat works by simply returning a function, it’s possible to modify it to do more complex behaviour like playing a custom sound.

on_place and on_secondary_use

The difference between on_place and on_secondary_use is that on_place is called when the player is pointing at a node and on_secondary_use when the player isn’t.

Both callbacks are called for all types of items. on_place defaults to the core.item_place function, which handles calling the on_rightclick callback of the pointed node or placing the wielded item if it is a node.

on_drop

on_drop is called when the player requests to drop an item, for example using the drop key (Q) or dragging it outside of the inventory. It defaults to the core.item_drop function, which will handle dropping the item.

after_use

after_use is called when digging a node and allows you to customise how wear is applied to a tool. If after_use doesn’t exist, then it is the same as:

after_use = function(itemstack, user, node, digparams)
    itemstack:add_wear(digparams.wear)
    return itemstack
end

item_place vs place_item

Minetest’s API includes many different built-in callback implementations for you to use. These callbacks are named with the item type first, for example, core.item_place and core.node_dig. Some callback implementations are used directly whereas some are functions that return the callback:

core.register_item("mymod:example", {
    on_place = core.item_place,
    on_use = core.item_eat(10),
})

Minetest’s API also includes built-in functions that do something. These are often named in a confusingly similar way to built-in callback implementations but have the verb first. Examples include core.place_item and core.dig_node - these functions allow you to dig and place nodes with a similar effect to players.

Node Callbacks

When a node is in an inventory, it uses Item Callbacks, as discussed above. When a node is placed in the world, it uses Node Callbacks. There are quite a lot of node callbacks, too many to discuss in this book. However, quite a few of them will be talked about later in the book.

Several of the callbacks are related to node operations such as placing and removing from the world. It’s important to note that node operation callbacks like these aren’t called from bulk changes - those that set a large number of nodes at once - for performance reasons. Therefore, you can’t rely on these callbacks to always be called.

Right-clicking and placing a node

When the user right-clicks with an item whilst pointing at a node, the item’s on_place callback is called. By default, this is set to core.item_place. If the pointed node has an on_rightclick callback and sneak (shift) is held, then the on_rightclick callback is called. Otherwise, core.item_place will place the node.

Placing a node will call both on_construct and after_place_node. on_construct is called by any node set event that wasn’t in bulk and is just given the node’s position and value .after_place_node is only called by node place, and so has more information - such as the placer and itemstack.

It’s important to note that players aren’t the only objects that can place nodes; it’s common for mobs and mods to place nodes. To account for this, placer could be a player, entity, or nil.

core.register_node("mymod:mynode", {
    on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
        if clicker:is_player() then
            core.chat_send_player(clicker:get_player_name(), "Hello world!")
        end
    end,
    on_construct = function(pos, node)
        local meta = core.get_meta(pos)
        meta:set_string("infotext", "My node!")
    end,
    after_place_node = function(pos, placer, itemstack, pointed_thing)
        -- Make sure to check placer
        if placer and placer:is_player() then
            local meta = core.get_meta(pos)
            meta:set_string("owner", placer:get_player_name())
        end
    end,
})

Punching and digging

Punching is when the player left-clicks for a short period. If the wielded item has an on_use callback, this will be called. Otherwise, the on_punch callback on the pointed node will be called.

When the player attempts to dig a node, the on_dig callback on the node will be called. This defaults to core.node_dig, which will check for area protection, wear out the tool, remove the node, and run the after_dig_node callback.

core.register_node("mymod:mynode", {
    on_punch = function(pos, node, puncher, pointed_thing)
        if puncher:is_player() then
            core.chat_send_player(puncher:get_player_name(), "Ow!")
        end
    end,
})

…and more!

Check out Minetest’s Lua API reference for a list of all node callbacks, and more information on the callbacks above.