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.exein it. - The CLight SDK. It ships with Celestial, in the
clight-sdkfolder next toCelestial.exe. CLight is the name of the modding system, and the SDK is its toolbox: theclighttool, the headers (including the C++ wrapperclight.hpp), templates and examples. - wasi-sdk, the C and C++ compiler for WebAssembly. The
clighttool 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:
- Press the Windows key and search for Edit environment variables for your account.
- Select Path, click Edit, then New, and paste the full path of the
binfolder, for exampleC:\Games\Celestial\clight-sdk\bin. - 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 with the C++ standard library), 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. (clight.h is the C API; clight.hpp sits next to it in the same folder.)
Make a new mod
Go to the folder where you keep your projects and run clight new cpp:
> clight new cpp my_mod
created C:\Projects\my_mod (cpp, 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 cpp ruby_tools --name "Ruby Tools"
What’s inside
my_mod/
clight.mod.json the manifest: id, version, name, licence, ...
src/
main.cpp 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 .cpp files. clight build compiles every .cpp, .cc, .cxx and .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.cpp. The template already does something useful:
// My Mod: a CLight mod in C++, compiled to WebAssembly by `clight build`.
// clight.hpp wraps the C API: plain strings, RAII handles, lambdas.
#include <clight.hpp>
class Mod final : public clight::Mod {
public:
// Runs once when the mod loads: subscribe to events here.
void on_construct() override {
clight::log::info("{} {} loaded", clight::mod_id(), clight::mod_version());
// When a player joins, say hello.
clight::on<clight::ev::PlayerJoin>([](cl_ev_player& event) {
clight::Entity player = event.player;
player.send_message("Welcome, " + player.name() + "!");
// translated instead: player.send_message(clight::text::translatable("my_mod.welcome"));
}).detach(); // keep the handler for the mod's lifetime
}
};
CLIGHT_MOD(Mod)
Read it from the bottom up:
CLIGHT_MOD(Mod)tells the game which class is your mod. When the mod loads, it creates one object of that class. It takes the place ofmain, and it must appear exactly once in your mod.class Mod final : public clight::Modis your mod.clight::Modis a base class with avirtualfunction for each moment of the game’s startup. You override the ones you need. (The template calls its classModtoo; insideclight::and outside it, those are two different names.)on_constructruns once, right when the mod loads. It tells the game what the mod wants to hear about.clight::on<clight::ev::PlayerJoin>(...)means “when a player joins, call this lambda”. The lambda gets the event’s details asevent..detach()keeps the handler for as long as the mod runs. Without it, the handler would be removed again at the end of the line, and the compiler warns you about that. Listening to events explains why.clight::Entity player = event.player;wraps the player in a small class with useful methods, likename()andsend_message().clight::log::infowrites a line to the game’s log.{}is replaced by the next value, like instd::format.
You will meet all of these properly in The C++ wrapper.
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", "-fno-exceptions"],
"cppStandard": "c++23"
}
]
}
clight.hpp is generated from the same description as clight.h, and every C function in clight.h has a short comment: what it does, where it may run, and since which API version it exists. When you wonder what a wrapper method does, look up the C function it calls; the names match (player.send_message calls cl_player_send_message).
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 cpp hello_world --name "Hello World"
In src/main.cpp, change only the message line:
player.send_message("Welcome, " + player.name() + "! Hello World is installed.");
player.name() gives a std::string, so + joins the pieces into one string (Concatenation on W3Schools). 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 cpp <id>, and write your mod as a class in src/main.cpp, created by CLIGHT_MOD.