Skip to content

Setting up

Lesson 3.1

In this lesson you install the tools, make a new C mod and look at what is inside it.

What you need

  • Celestial itself. The download from the Celestial website is a folder with Celestial.exe in it.
  • The CLight SDK. It ships with Celestial, in the clight-sdk folder next to Celestial.exe. CLight is the name of the modding system, and the SDK is its toolbox: the clight tool, the C headers, templates and examples.
  • wasi-sdk, the C compiler for WebAssembly. The clight tool downloads it for you.
  • A text editor. Visual Studio Code with Microsoft’s C/C++ extension works well, but any editor is fine.

You don’t need Visual Studio, MinGW or any other compiler. C mods are not Windows programs: they are compiled to WebAssembly (WASM), a portable format that the game runs in a sandbox. That is why one .clmod file works for every player.

Put the clight tool on your PATH

The SDK’s command-line tool is clight-sdk/bin/clight.exe. Add that bin folder to your PATH so you can type clight in any terminal:

  1. Press the Windows key and search for Edit environment variables for your account.
  2. Select Path, click Edit, then New, and paste the full path of the bin folder, for example C:\Games\Celestial\clight-sdk\bin.
  3. Click OK and open a new terminal window.

Install the compiler

Run clight setup once:

> clight setup
downloading https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-windows.tar.gz
verified sha256, extracting...
installed wasi-sdk-34 at C:\Games\Celestial\clight-sdk\wasi-sdk

It downloads the official wasi-sdk release from GitHub, checks it against a known checksum, and unpacks it into clight-sdk/wasi-sdk. It is a big download (a full clang compiler), so give it a minute. You only do this once; clight setup --force installs it again if something went wrong.

If you already have wasi-sdk somewhere else, set the WASI_SDK_PATH environment variable to its folder, and clight uses that one instead.

Now check the whole installation:

> clight doctor
CLight SDK 0.1.0 (API 1.24)
  ok   SDK folder  C:\Games\Celestial\clight-sdk
  ok   clight.h  C:\Games\Celestial\clight-sdk\include
  ok   wasi-sdk (C/C++ mods)  C:\Games\Celestial\clight-sdk\wasi-sdk 34.0
  ok   Celestial  C:\Games\Celestial\Celestial.exe
  Lua mods need nothing else.

All four lines should say ok. A MISSING line tells you what to do next.

Make a new mod

Go to the folder where you keep your projects and run clight new c:

> clight new c my_mod
created C:\Projects\my_mod (c, 5 files)
next:  cd my_mod  &&  clight build  &&  clight run

my_mod is the mod’s id. Every mod has one, and it has to be unique. The rules: lowercase letters, digits and _, starting with a letter, 2 to 64 characters. ruby_tools is fine, RubyTools and 2cool are not.

The display name is made from the id (my_mod becomes “My Mod”). Pick another with --name:

> clight new c ruby_tools --name "Ruby Tools"

What’s inside

my_mod/
  clight.mod.json            the manifest: id, version, name, licence, ...
  src/
    main.c                   your code starts here
  assets/
    my_mod/
      lang/
        en_us.json           texts players see, by language
  CMakeLists.txt             only if you want to build with CMake (optional)
  .gitignore

A bigger mod adds more folders next to these:

Folder What goes in it
src/ Your .c files. clight build compiles every .c file in here, also in subfolders. Headers you write go here too.
assets/<id>/ Resource-pack files: textures, models, sounds, language files, GUI files.
datapack/<id>/ Data-pack files: recipes, loot tables, tags.
icon.png The mod’s icon, square, 64 to 256 pixels.
LICENSE The licence text.
build/ Made by clight: object files, the compiled bin/my_mod.wasm and the packed mod. Don’t edit it.

Your first source file

Open src/main.c. The template already does something useful:

// My Mod: a CLight mod in C, compiled to WebAssembly by `clight build`.
// clight_easy.h has short helpers for the common tasks; clight.h is the full API.
#include <clight_easy.h>

// When a player joins, say hello.
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_send_messagef(player, "Welcome, %s!", name);
}

// Runs once when the mod loads: subscribe to events here.
static cl_result mod_main(void) {
    CL_LOG("My Mod loaded");
    return CL_ON(CL_EV_PLAYER_JOIN, on_player_join);
}

CLIGHT_MOD_ENTRY(mod_main)

Read it from the bottom up:

  • CLIGHT_MOD_ENTRY(mod_main) tells the game which function to call when your mod loads. It takes the place of main. It must appear exactly once in your mod.
  • mod_main runs once. It tells the game what the mod wants to hear about, and returns CL_OK if everything went well. Returning anything else stops the mod from loading.
  • CL_ON(CL_EV_PLAYER_JOIN, on_player_join) means “when a player joins, call on_player_join”. It returns CL_OK when the subscription worked, so mod_main can return its result directly.
  • on_player_join is a callback. The game calls it with the event’s details in payload.
  • CL_LOG writes a line to the game’s log, like printf.

The (void)user; line only tells the compiler “I know I don’t use this parameter”, so it doesn’t warn. You will meet CL_EVENT, CL_ON and the cl_easy_ helpers properly in The easy header.

The sandbox

Your mod runs in a sandbox. It can call the game API and read its own folder, and nothing else: no other files, no programs, no network, no native code. A mod can’t read your saves or your account. That is why players can install mods without worrying.

Editor help

To get completion and error squiggles, tell your editor where the headers are. With VS Code and the C/C++ extension, create .vscode/c_cpp_properties.json in your mod folder (change the paths to yours):

{
  "configurations": [
    {
      "name": "CLight",
      "includePath": ["${workspaceFolder}/src", "C:/Games/Celestial/clight-sdk/include"],
      "defines": ["__wasm__"],
      "compilerPath": "C:/Games/Celestial/clight-sdk/wasi-sdk/bin/clang.exe",
      "compilerArgs": ["--target=wasm32-wasip1"],
      "cStandard": "c17"
    }
  ]
}

The header has short comments above many functions. Hover over a function to read what it does, where it may run, and since which API version it exists.

Try it

Make a mod called hello_world with the display name “Hello World”. Change the welcome message so it says “Welcome, Alex! Hello World is installed.” (with the player’s real name).

Show a solution
> clight new c hello_world --name "Hello World"

In src/main.c, change only the message line:

cl_easy_send_messagef(player, "Welcome, %s! Hello World is installed.", name);

cl_easy_send_messagef works like printf: %s is replaced by name. The next lesson shows how to build the mod and see the message in the game.

Recap: put clight-sdk/bin on your PATH, run clight setup once for the compiler, make a mod with clight new c <id>, and write your code in src/main.c, starting at CLIGHT_MOD_ENTRY.