Skip to content

The manifest

Lesson 3.2

Every mod has a file called clight.mod.json in its top folder. It is the mod’s passport: the game reads it before it loads any code, and the Celestial website reads it when you upload the mod.

The template’s manifest

{
  "schema": 1,
  "id": "my_mod",
  "version": "1.0.0",
  "name": "My Mod",
  "description": "A CLight mod written in C (WebAssembly).",
  "authors": [],
  "license": "MIT",
  "api": 1,
  "game": ">=26.3 <26.4",
  "side": "both",
  "sync": "optional",
  "runtime": { "type": "wasm", "entry": "bin/my_mod.wasm" },
  "permissions": []
}

It is JSON, not C: keys and strings use double quotes, there are no comments, and there is no comma after the last entry.

The fields

Field What it means
schema The manifest format. Always 1.
id The mod’s id. It must match the folder name under assets/ and datapack/, and it can’t change once the mod is published.
version The mod’s version, like 1.0.0. Raise it for every release.
name The name players see.
description One or two sentences about the mod.
authors A list of names: ["Alex", "Sam"].
license The licence: an SPDX id such as "MIT", "Apache-2.0", or "All-Rights-Reserved".
api The CLight API your mod is written for. Use 1.
game The game versions the mod works with.
side Where the mod runs: "client", "server" or "both".
sync Whether players need the mod to join a server that has it.
runtime "type": "wasm" for C, and the compiled file the game loads.
permissions Extra things the mod asks the player for. Usually empty.

runtime: where the WASM file is

For a C mod, runtime always looks like this:

"runtime": { "type": "wasm", "entry": "bin/my_mod.wasm" }

entry is the path inside the packed mod. clight build compiles your sources to build/bin/my_mod.wasm and packs that file as bin/my_mod.wasm, so the template’s value is right. Keep it as bin/<your id>.wasm. If you rename your mod, change it here too.

Versions

Versions follow SemVer: three numbers, major.minor.patch.

  • Fixed a bug? Raise the last number: 1.0.0 to 1.0.1.
  • Added something new? Raise the middle one and reset the last: 1.0.1 to 1.1.0.
  • Changed things so old worlds or other mods may break? Raise the first: 1.1.0 to 2.0.0.

The website won’t accept the same version twice, so a new upload always needs a new number.

“api”: one mod, every build

"api": 1 means “this mod uses CLight API 1”. The API only ever grows: functions are added, never removed or changed. So a mod built for API 1 keeps working on every later Celestial build with API 1, without you rebuilding it.

Your compiled mod also tells the game which API version its headers came from. CLIGHT_MOD_ENTRY does that for you. A mod built with newer headers than the player’s game refuses to load with a clear message, instead of crashing later.

New functions arrive in minor versions (1.1, 1.2, …). The header marks each one with the version it arrived in. If you use a newer function and still want to load on older builds, ask first:

if (cl_core_has(CL_STR("sound"))) {
    // the sound module exists in this game
}

cl_core_has knows modules ("gui", "sound"), single functions ("level.set_block"), sides ("side.client") and features ("hot_reload.wasm"). Unknown names give false.

side and sync

side decides where the mod is loaded:

side In the game you play On a dedicated server
"client" yes no
"server" yes (in singleplayer worlds) yes
"both" yes yes

Most mods use "both": a new item has to exist on the server (which owns the world) and on the client (which draws it). A mod that only adds a HUD or a keybind can be "client". Contexts and sides explains the two halves.

sync says whether a player needs the mod to join a server that runs it: "required", "optional", "client_only" or "server_only". The website shows it on the mod’s page.

More fields

You won’t need these at first, but they exist:

Field Example Use
depends { "other_mod": ">=1.2" } Mods that must be installed. Without them your mod doesn’t load.
optional { "other_mod": ">=1.0" } Mods you work with if they are there.
breaks { "old_mod": "*" } Mods that can’t run next to yours.
icon "icon.png" The icon file, if it isn’t the default icon.png.
links { "homepage": "https://...", "issues": "https://..." } Shown on the mod’s page.
limits { "memory_mb": 64 } The mod’s memory limit in MB. The default is 256.
custom see below Extra compiler or linker flags.

Extra flags for clight build go into custom.clight_build:

"custom": {
  "clight_build": {
    "cflags": ["-Wall", "-Wextra", "-DRUBY_DEBUG=1"],
    "ldflags": []
  }
}

-Wall -Wextra is a good idea: the compiler warns about many small mistakes before they reach the game.

clight check tells you about mistakes in the manifest before the game or the website does:

> clight check
ok: my_mod 1.0.0

Try it

Your mod ruby_tools should be version 0.2.0, written by you, MIT licensed, needed on both sides, and players should need it to join. Turn on compiler warnings too. Write the manifest.

Show a solution
{
  "schema": 1,
  "id": "ruby_tools",
  "version": "0.2.0",
  "name": "Ruby Tools",
  "description": "Ruby gear and a ruby counter.",
  "authors": ["Alex"],
  "license": "MIT",
  "api": 1,
  "game": ">=26.3 <26.4",
  "side": "both",
  "sync": "required",
  "runtime": { "type": "wasm", "entry": "bin/ruby_tools.wasm" },
  "permissions": [],
  "custom": { "clight_build": { "cflags": ["-Wall", "-Wextra"] } }
}

Run clight check to make sure it is valid. Note the entry: it uses the new id.

Recap: clight.mod.json names your mod and says where it runs; for C, runtime is wasm with entry bin/<id>.wasm, raise version for every release and keep "api": 1.