Skip to content

Messages and text

Lesson 6.2

Chat messages, action-bar hints, command replies: mods talk to players a lot. The easy helpers send plain text. For colours, translations and texts built from parts, the API has text components.

Plain messages

You know these already:

cl_easy_send_message(player, "Hello!");                 // chat
cl_easy_send_messagef(player, "You have %d rubies.", n); // chat, with formatting
cl_easy_send_action_bar(player, "Ready.");               // above the hotbar
cl_easy_broadcast("The event starts now!");              // chat, to everyone

They are all server-side.

Text components

A text (cl_text) is what Minecraft really sends: a piece of text with a style, which can have more pieces attached. You create one, send it, and release it:

cl_text t = cl_text_literal(CL_STR("Hello!"));
cl_player_send_message(player, t);
cl_text_release(t);

That is exactly what cl_easy_send_message does inside. The text functions:

Function Does
cl_text_literal(str) A text with exactly these characters. Owned.
cl_text_translatable(key) A text looked up in the player’s language file. Owned.
cl_text_color(t, 0xRRGGBB) Sets the colour.
cl_text_append(parent, child) Attaches a child text after the parent.
cl_text_to_plain(t, buf, cap, &len) The text without styles, as a C string.
cl_text_release(t) Gives it back.

And where texts go:

Function Shows it
cl_player_send_message(p, t) In the player’s chat.
cl_player_send_action_bar(p, t) Above the player’s hotbar.
cl_server_broadcast(t) In everybody’s chat.
cl_cmd_send_success(ctx, t, false) / cl_cmd_send_failure(ctx, t) As a command reply.

None of them take ownership: you still release the text afterwards.

Colours

Colours are written 0xRRGGBB: red, green and blue, each from 00 to FF.

cl_text t = cl_text_literal(CL_STR("Ruby found!"));
cl_text_color(t, 0xE0115F);   // ruby red
cl_player_send_message(player, t);
cl_text_release(t);

A few vanilla chat colours: green 0x55FF55, yellow 0xFFFF55, red 0xFF5555, aqua 0x55FFFF, gold 0xFFAA00, grey 0xAAAAAA.

Texts from parts

cl_text_append builds a message with several colours. The child inherits the parent’s style unless it sets its own:

static void send_score(cl_entity player, int score) {
    char num[16];
    snprintf(num, sizeof num, "%d", score);

    cl_text msg = cl_text_literal(CL_STR("Your score: "));
    cl_text_color(msg, 0xAAAAAA);

    cl_text value = cl_text_literal(CL_STR_C(num));
    cl_text_color(value, 0xFFAA00);
    cl_text_append(msg, value);      // msg keeps its own reference to value
    cl_text_release(value);          // so we can let go of ours right away

    cl_player_send_message(player, msg);
    cl_text_release(msg);
}

cl_text_append takes its own reference to the child. That is why value can be released straight after appending: msg keeps it alive. Releasing msg at the end frees both.

Translations

Mods are played in many languages. Instead of writing English into your code, write a translation key, and put the texts into language files:

cl_text t = cl_text_translatable(CL_STR("my_mod.welcome"));
cl_player_send_message(player, t);
cl_text_release(t);

assets/my_mod/lang/en_us.json:

{
  "my_mod.welcome": "Welcome to the server!"
}

assets/my_mod/lang/de_de.json:

{
  "my_mod.welcome": "Willkommen auf dem Server!"
}

Each player sees the text in their own language, and falls back to English when their language is missing. The key is sent as it is, so players without your mod see my_mod.welcome. For messages from a server-side mod that players may not have, use literal text.

Values in translations

For “You have 3 rubies”, the language file marks the value with %s, and cl_text_add_arg adds it. Like cl_text_append, it takes its own reference:

{ "my_mod.rubies": "You have %s rubies." }
cl_text t = cl_text_translatable(CL_STR("my_mod.rubies"));
cl_text n = cl_text_literal(CL_STR("3"));
cl_text_add_arg(t, n);
cl_text_release(n);
cl_player_send_message(player, t);
cl_text_release(t);

Several cl_text_add_arg calls fill %s in order; a language may also write %2$s and %1$s.

Styles, clicks and hovers

cl_text_set_style(t, styles, on) switches styles on or, with false, explicitly off. Combine them with |:

cl_text_set_style(t, CL_STYLE_BOLD | CL_STYLE_UNDERLINED, true);
cl_text_set_style(t, CL_STYLE_ITALIC, false);   // item names are italic by default

The styles are CL_STYLE_BOLD, CL_STYLE_ITALIC, CL_STYLE_UNDERLINED, CL_STYLE_STRIKETHROUGH and CL_STYLE_OBFUSCATED (the “magic” changing letters).

cl_text_click says what clicking the text does, and cl_text_hover sets the tooltip (it takes its own reference):

cl_text link = cl_text_literal(CL_STR("[Open the wiki]"));
cl_text_click(link, CL_TEXT_CLICK_OPEN_URL, CL_STR("https://example.com/ruby-wiki"));
cl_text tip = cl_text_literal(CL_STR("Opens the wiki in your browser"));
cl_text_hover(link, tip);
cl_text_release(tip);
cl_player_send_message(player, link);
cl_text_release(link);
Action A click
CL_TEXT_CLICK_OPEN_URL asks, then opens the address (https:// or http:// only; anything else is CL_E_INVALID_ARGUMENT)
CL_TEXT_CLICK_RUN_COMMAND runs the command as the player ("/spawn")
CL_TEXT_CLICK_SUGGEST_COMMAND puts the text into the chat box
CL_TEXT_CLICK_COPY_TO_CLIPBOARD copies the value

Titles

cl_easy_send_title(player, "Round 2", "Get ready!");   // vanilla's times

cl_player_send_title(player, title, subtitle, fade_in, stay, fade_out) takes texts and times in ticks; -1 keeps the player’s current time, and a null text ((cl_text){0}) keeps the current title or subtitle. cl_player_clear_title(player, reset_times) removes it.

Messages only this player sees

On the client, cl_client_show_message(text, overlay) shows a chat line (overlay = true: above the hotbar) without sending anything to the server, and cl_client_show_title(...) a title. cl_easy_show_message("Zoom on") is the short form. They work on any server, even one without your mod.

Reading a text

cl_text_to_plain turns a text back into a C string, without colours:

char plain[256];
size_t len = 0;
cl_text_to_plain(msg, plain, sizeof plain, &len);
CL_LOG("sent: %s", plain);

Try it

Write void announce(const char* who, const char* what) that sends everyone a message like Alex found a diamond!, where the name is gold, the thing is aqua and the rest is white.

Show a solution
static void add_part(cl_text parent, const char* s, cl_color color) {
    cl_text part = cl_text_literal(CL_STR_C(s));
    cl_text_color(part, color);
    cl_text_append(parent, part);
    cl_text_release(part);
}

static void announce(const char* who, const char* what) {
    cl_text msg = cl_text_literal(CL_STR(""));
    cl_text_color(msg, 0xFFFFFF);
    add_part(msg, who, 0xFFAA00);
    add_part(msg, " found ", 0xFFFFFF);
    add_part(msg, what, 0x55FFFF);
    add_part(msg, "!", 0xFFFFFF);
    cl_server_broadcast(msg);
    cl_text_release(msg);
}

The parent is an empty text that only holds the parts. A small helper like add_part keeps the create, colour, append, release steps in one place, so you can’t forget a release.

Recap: the easy helpers send plain strings; cl_text_literal and cl_text_translatable make texts you colour with cl_text_color, combine with cl_text_append, send with cl_player_send_message or cl_server_broadcast, and release afterwards.