Skip to content

Overview: what CLight is

Status: Implemented (API 1.24). The model on this page is what ships. Each API page says how much of its area exists; parts marked Planned are not in the game yet.

CLight is Celestial’s mod loader and modding API. It loads mods: packages that add content, change gameplay, extend the client’s rendering and UI, or run as server plugins.

Every mod runs in a sandbox

There is no “trusted native code” in CLight. Every mod, in every language, runs inside a container that protects the player from a malicious or broken mod:

  • A mod can read only its own folder (mods/<id>/) and write only its own data folder (mods/<id>/data/). No other files, no other mods’ folders, no saves, no system folders.
  • A mod can’t start programs, load DLLs, open sockets or read environment variables.
  • A mod has no network access of its own. With the net.http:<host> capability the player allowed, it may make HTTPS requests to that host (API 1.20), and downloads go only into mods/<id>/data/. Mods talk to their own server side through payloads (Networking).
  • Memory and CPU time are limited per mod (limits.memory_mb, default 256 MB; a Lua callback that runs longer than limits.callback_ms, default 50 ms, at most 500, is stopped). A mod that traps or hangs is turned off for the session; the game keeps running.

The extra rights a mod can ask for are listed on the Permissions screen (Mods → select a mod → Permissions), and each one is the player’s decision (Capabilities and permissions).

How a mod plugs into Celestial

           ┌─────────────────────── Celestial process ────────────────────────┐
           │                                                                 │
 mods/     │   CLight loader ── resolves, sorts, loads                       │
 *.clmod ──┼──►    │                                                         │
 <id>/     │       ├─ data/assets ─────────► pack stack (resources, data)    │
           │       ├─ Lua VM (sandboxed) ──┐                                 │
           │       └─ Wasmtime (sandboxed) ┴──► CLight API (clight.idl)      │
           │                                    │ events · registries ·      │
           │                                    ▼ behaviour overrides        │
           │                        engine: registries, levels, job system,  │
           │                        Vulkan renderer, RmlUi GUI, network      │
           └─────────────────────────────────────────────────────────────────┘

Every runtime calls the same API: functions and events defined once in clight.idl. Only the calling mechanism differs:

Runtime Used for Call path into the engine Isolation
none (data) Assets and data packs only The engine reads your files Complete (data only)
Lua 5.4, one VM per mod (lua) Lua mods Binding functions registered in the VM No io, no package, os reduced to time functions, no bytecode loading; require loads only the mod’s own scripts; memory and time limits
Wasmtime, AOT-compiled and cached (wasm) C and C++ mods (and anything else that compiles to WebAssembly) WASM imports named exactly like the C functions (clight.cl_*) Linear memory, CLight imports only; WASI sees the mod folder read-only as / and mods/<id>/data/ as /data; memory limit and a time limit per call

Mods change the game through events (with priorities and cancellation), registries (blocks, items, entities, …) and behaviour overrides (callbacks a registered block, item or entity runs instead of the default). For deeper changes there are mixins (the core shipped in API 1.2): sandboxed callbacks at named, versioned points inside engine functions (Mixins). The game’s mixin targets arrive in batches; cl.has("mixin:<name>") tells you which exist. Nothing patches the game’s machine code.

The three languages

Language Runtime Strengths Limits
C WebAssembly Smallest, most portable. The ABI itself. Verbose
C++ WebAssembly Fast (near-native with AOT), RAII wrapper, lambdas, Expected A build step (clight build); no exceptions
Lua Lua VM No build step, hot reload on save, the shortest code (player:give("minecraft:apple")) Slower for heavy maths

A .clmod built from C or C++ is one file for every operating system: the same .wasm runs everywhere Celestial runs.

Mods in different languages share the same registries, events and payload channels. A dedicated services API for calling another mod is planned (Services and mod interop).

What you can build

Today (API 1.24):

  • Content: blocks and items (each backed by a vanilla block or item in the world), their behaviour, block loot tables, and everything a data pack can do (recipes, loot, tags, advancements, enchantments) from the mod’s datapack/ folder; textures, models, lang files and custom sounds and music from assets/
  • Entities: custom entity types with glTF models and layered animation, gameplay (attributes, effects, damage, synced data), pathfinding and AI goals, player camera and movement control, saved data and attachments
  • Gameplay changes: events (joins, chat, commands, block break and place, interactions, damage, death, ticks, entity spawn, …) with priorities and cancellation, plus mixins on 93 targets in game logic, the client, world generation, chunks, recipes, loot and tags
  • Client mods: HUD layers and HUD elements, screens with vanilla widgets, the UI kit, components and sandboxed HTML overlays, themes and Appearance pages, particles and decals, post-processing passes, fog, sky and camera effects, item and block models, keybinds in the Controls screen, sounds and music, a config page in the Mods screen
  • Server mods: commands with arguments and permissions, container menus (shops, kits), titles, inventories, scheduled tasks, payloads to Celestial clients
  • Libraries: mods that others depend on (depends in the manifest)

Still planned: block entity types for mod blocks, dimensions and a worldgen API in code (mixins on the world-generation targets work today), more render stages, and a services API between mods. The API pages mark these as Planned.

The rules every mod lives under

  1. Server authority. The logical server owns the world, and clients display it (Sides and environments).
  2. Thread contexts. Every callback runs in a declared context and may only touch what that context owns (Threading model).
  3. Budgets. Each mod has a memory limit and a time limit per callback. Long work belongs in scheduled steps, not in one callback.
  4. Determinism. Use game RNGs and game time (Determinism and vanilla parity).
  5. Zero cost when unused. With no mods installed the game is exactly vanilla (same image, same packets) and pays nothing. Mods pay only for the events they subscribe to.

Next