Skip to content

The easy header

Lesson 4.1

The CLight API is big and exact: every function says precisely what it takes and gives back. That is great for big mods, but it means a few lines of code for small jobs like “give this player an apple”. The SDK’s clight_easy.h does those jobs in one line. This lesson shows everything it has.

One include for everything

#include <clight_easy.h>

clight_easy.h includes clight.h, the full API, so with this one line you have both. Use the easy helpers where they fit, and the full functions for everything else. You can mix them freely.

Handlers and CL_ON

Your mod reacts to the game through handlers: functions the game calls when something happens. Every handler has the same shape:

static void on_something(void* payload, void* user) {
    // payload: the event's details
    // user: your own pointer (see Listening to events)
}

CL_ON(event, handler) connects a handler to an event:

CL_ON(CL_EV_PLAYER_JOIN, on_player_join);
CL_ON(CL_EV_BLOCK_BREAK, on_block_break);

It returns CL_OK if it worked. Every event has a name starting with CL_EV_; The event list has them all.

CL_EVENT: reading the payload

The payload arrives as void*, because one handler type serves every event. Each event has its own struct, and CL_EVENT turns the pointer into it:

static void on_block_break(void* payload, void* user) {
    (void)user;
    cl_ev_block_break* e = CL_EVENT(cl_ev_block_break, payload);
    CL_LOG("block broken at %d %d %d", e->pos.x, e->pos.y, e->pos.z);
}

CL_EVENT(cl_ev_block_break, payload) is the same as (cl_ev_block_break*)payload, just easier to read. The event list tells you which struct belongs to which event: CL_EV_PLAYER_JOIN uses cl_ev_player, CL_EV_BLOCK_BREAK uses cl_ev_block_break, and so on. Using the wrong struct reads garbage, so check the list.

Because e is a pointer, you read fields with ->. If that is still new, read Struct pointers on W3Schools.

CL_LOG

You met it already: printf-style logging at the info level. CL_LOGF(level, ...) picks the level.

CL_LOG("%d players online", 3);
CL_LOGF(CL_LOG_WARN, "that's odd: %s", "no rubies");

The cl_easy_ helpers

Helper Does
cl_easy_player_name(player, buf, size) Copies the player’s name into your char array. Returns its length.
cl_easy_give(player, "minecraft:apple", 3) Gives items. What doesn’t fit is dropped at the player’s feet.
cl_easy_send_message(player, "Hi!") A chat message to one player.
cl_easy_send_messagef(player, "Hi %s!", name) The same with printf formatting (up to 511 bytes).
cl_easy_send_action_bar(player, "Hi!") A message above the hotbar.
cl_easy_broadcast("Hello everyone") A chat message to every player on the server.
cl_easy_set_block(level, pos, "minecraft:stone") Places a block, updating neighbours and players.
cl_easy_stack_is(stack, "minecraft:apple") true if an item stack holds that item.

All of them take plain C strings ("..." or a char array), and they all return a cl_result (CL_OK when it worked), except the two that return a length or a bool.

Item and block ids are written namespace:name. Vanilla things use minecraft:, your own things use your mod id: "my_mod:ruby".

A small mod with only easy helpers

#include <clight_easy.h>

// A gift for everyone who joins, and a hello to the whole server.
static void on_player_join(void* payload, void* user) {
    (void)user;
    cl_entity player = CL_EVENT(cl_ev_player, payload)->player;
    char name[64];
    cl_easy_player_name(player, name, sizeof name);

    cl_easy_give(player, "minecraft:bread", 3);
    cl_easy_send_action_bar(player, "Here is some bread for the road.");

    char line[128];
    snprintf(line, sizeof line, "%s just arrived!", name);
    cl_easy_broadcast(line);
}

static cl_result mod_main(void) {
    return CL_ON(CL_EV_PLAYER_JOIN, on_player_join);
}

CLIGHT_MOD_ENTRY(mod_main)

snprintf comes from <stdio.h>, which the header includes too. It writes formatted text into an array and never writes past its end; see Output and Format specifiers for the % codes.

When you need more

The easy helpers cover the everyday jobs. For everything else, you use clight.h directly: coloured text, inventories, blocks you read and compare, commands, screens and much more. Each easy helper is only a few lines over the full API, and the next lessons explain the ideas behind it: Handles and strings, Errors, and Contexts and sides. Once you know those, every function in the header reads the same way.

Try it

When a player joins, give them one minecraft:compass and send them two chat messages: “Welcome, NAME!” and “Your compass points to spawn.”

Show a solution
#include <clight_easy.h>

static void on_player_join(void* payload, void* user) {
    (void)user;
    cl_entity player = CL_EVENT(cl_ev_player, payload)->player;
    char name[64];
    cl_easy_player_name(player, name, sizeof name);

    cl_easy_give(player, "minecraft:compass", 1);
    cl_easy_send_messagef(player, "Welcome, %s!", name);
    cl_easy_send_message(player, "Your compass points to spawn.");
}

static cl_result mod_main(void) {
    return CL_ON(CL_EV_PLAYER_JOIN, on_player_join);
}

CLIGHT_MOD_ENTRY(mod_main)

The first message needs formatting, so it uses cl_easy_send_messagef. The second is fixed text, so the plain cl_easy_send_message is enough.

Recap: #include <clight_easy.h> gives you the whole API plus CL_ON, CL_EVENT, CL_LOG and cl_easy_ helpers that do common jobs with plain strings in one line.