Skip to content

Events, behaviours and mixins

Status: Implemented for events, behaviour callbacks on your own blocks and items, and the mixin core. Overrides of vanilla content’s behaviour tables and value events are Planned; mixins on game targets cover part of that today.

CLight has three ways to change the game. Start with the simplest one that does the job:

Mechanism Changes Example
Events (Events) Whether something happens, and the documented mutable values Cancel a block break, change the XP dropped, block a chat message
Behaviour callbacks How your own block or item reacts Right-click your block to open a shop; a wand item that does something on use
Mixins (Mixins) A step inside a game function, at a named, versioned target Change a damage amount inside hurt, skip a vanilla step

Plus registries for new content (Registries). All of these are explicit points the engine publishes; nothing patches the game’s machine code, and two mods touching the same point compose in priority order.

Behaviour callbacks

A registered block or item can carry a behaviour table: a struct of callbacks the engine calls for that content only. Leave an entry NULL and vanilla behaviour (of the base block or item) applies.

Table Entries Called when
cl_block_behavior use_without_item(ctx, player, hand, out_result) A player right-clicks the block with an empty hand or an item that doesn’t use blocks
attack(ctx, player, out) A player left-clicks (starts breaking) it
random_tick(ctx, out) The block gets a random tick (set the CL_BLOCK_RANDOM_TICKS flag)
cl_item_behavior use(ctx, out_result) A player uses the item (right-click in the air or on a block)

Every entry returns cl_override: CL_OVERRIDE_HANDLED (you decided; the result goes through the last parameter) or CL_OVERRIDE_CALL_ORIGINAL (vanilla runs as if you weren’t there).

static cl_override shop_use(const cl_block_ctx* ctx, cl_entity player, cl_hand hand, cl_interaction_result* out) {
    (void)ctx; (void)hand;
    cl_easy_send_message(player, "Welcome to the shop!");
    *out = CL_RESULT_SUCCESS;
    return CL_OVERRIDE_HANDLED;
}

static cl_block_behavior g_shop = CL_BLOCK_BEHAVIOR_INIT;   /* must outlive the registration */

/* in lifecycle.register: */
g_shop.use_without_item = shop_use;
cl_block_desc d = CL_BLOCK_DESC_INIT;
d.id = CL_STR("my_shop:counter");
d.vanilla_fallback = CL_STR("minecraft:barrel");
d.behavior = &g_shop;
cl_block_register(&d, NULL);
cl.blocks.register{
  id = "counter", vanilla_fallback = "minecraft:barrel",
  behavior = {
    use_without_item = function(ctx, player, hand)
      player:send_message("Welcome to the shop!")
      return "success"   -- handled; return nothing to let the fallback block act
    end,
  },
}
clight::registry::block(d, {.use_without_item = [](const cl_block_ctx&, clight::Entity player, cl_hand, cl_interaction_result& out) {
    player.send_message("Welcome to the shop!");
    out = CL_RESULT_SUCCESS;
    return CL_OVERRIDE_HANDLED;
}});

The ctx (cl_block_ctx, cl_item_ctx) has the level, the position and state (blocks), or the stack, entity and hand (items). Lua behaviour functions are swapped in place on hot reload.

What can’t be changed, and why

Not changeable Reason
Arbitrary engine functions or code offsets No stable meaning across versions. Use a named mixin target; missing targets can be requested.
Protocol encoding and decoding Wire compatibility with vanilla servers and clients. Use payloads (Networking).
The job scheduler, allocators, the Vulkan device Engine invariants (GPU access).

Requesting new events

If you need an event, a behaviour entry or a mixin target that doesn’t exist, check Mixin targets first, then file a request with the vanilla class and method it would mirror. New entries are added in minor API versions and never removed within a major version (ABI stability and API versioning). cl.has("name") / cl_core_has tells a mod at run time whether the game it runs on has them.

Planned

  • Overrides of vanilla content (a behaviour chain on minecraft:sugar_cane, for example), with more entries (can_survive, entity_inside, …).
  • Value events: a mutable result that starts as the vanilla value (fall damage, mining speed, FOV), listed in Value event list.
  • Behaviour tables for entity types and effects.