Skip to content

Mod and lifecycle

Status: Implemented. Entry points, the current mod, lifecycle queries and every lifecycle event. Reading the mod’s files (cl_fs_*, Filesystem and HTTP) and the WebAssembly hot-reload state hand-over (clight_mod_save_state / clight_mod_restore_state, Hot reload) arrived in API 1.20. Mod metadata queries (info, list, version ranges) are Planned.

Function tables: core, mod. Concepts: Mod lifecycle.

Entry points (exports of a WASM mod)

Export Required Signature Generated by
clight_mod_api_version yes uint32_t clight_mod_api_version(void): the CLIGHT_API_VERSION the mod was built against CLIGHT_MOD_ENTRY
clight_mod_main yes cl_result clight_mod_main(void) CLIGHT_MOD_ENTRY(fn) calls fn()
clight_mod_unload no void clight_mod_unload(void) CLIGHT_MOD_UNLOAD(fn)
clight_guest_alloc, clight_guest_free yes The engine allocates strings and arrays it hands to the mod in guest memory CLIGHT_MOD_ENTRY
clight_mixins no The CL_MIXIN records of the module CLIGHT_MOD_ENTRY when the module declares mixins

C++ uses CLIGHT_MOD(Class), which defines all of them. Lua mods have no exports: the manifest’s runtime.entry script runs top to bottom.

The loader refuses a mod whose manifest "api" major is newer than the engine (CLL-2003) or older (CLL-2004) (Error codes). Within a major, use cl_core_has for functions added in later minor versions.

The current mod

The binding knows which mod is calling, so no function takes a mod parameter.

C C++ Lua Returns
cl_str cl_mod_id(void) clight::mod_id() mod.id Your mod id
cl_str cl_mod_version(void) clight::mod_version() mod.version Your version
bool cl_mod_is_loaded(cl_str id) clight::mods::is_loaded(id) cl.mod.is_loaded(id) Whether another mod is loaded
cl_mod_config_dir(buf, cap, &n) clight::mods::config_dir() cl.mod.config_dir() <game>/config/<modid>/ (created on demand)

Lifecycle queries

C Description Lua
cl_phase cl_lifecycle_phase(void) CL_PHASE_CONSTRUCT, REGISTER, FROZEN, SETUP, RUNNING, SHUTDOWN cl.mod.lifecycle_phase()
bool cl_registries_frozen(void) Blocks and items are frozen cl.registry.registries_frozen()
cl_env cl_environment(void) CL_ENV_CLIENT or CL_ENV_DEDICATED_SERVER cl.mod.environment()
bool cl_server_running(void) A logical server exists in this process cl.mod.server_running()
bool cl_is_dev(void) Running under clight run (--clight-dev) cl.mod.is_dev()
cl_context cl_current_context(void) The thread context of the current call cl.mod.current_context()

Lifecycle events

All lifecycle events use the payload cl_ev_lifecycle { cl_event_header header; cl_level level; } (level only for level_*). None can be cancelled.

Event C constant Context Side
lifecycle.register CL_EV_LIFECYCLE_REGISTER MAIN both
lifecycle.registries_frozen CL_EV_LIFECYCLE_REGISTRIES_FROZEN MAIN both
lifecycle.common_setup CL_EV_LIFECYCLE_COMMON_SETUP MAIN both
lifecycle.client_setup CL_EV_LIFECYCLE_CLIENT_SETUP MAIN client
lifecycle.server_setup CL_EV_LIFECYCLE_SERVER_SETUP MAIN server (dedicated)
lifecycle.resources_reloaded CL_EV_LIFECYCLE_RESOURCES_RELOADED MAIN client
lifecycle.data_reloaded CL_EV_LIFECYCLE_DATA_RELOADED MAIN server
lifecycle.server_starting CL_EV_LIFECYCLE_SERVER_STARTING MAIN server
lifecycle.server_started CL_EV_LIFECYCLE_SERVER_STARTED MAIN server
lifecycle.server_stopping CL_EV_LIFECYCLE_SERVER_STOPPING MAIN server
lifecycle.server_stopped CL_EV_LIFECYCLE_SERVER_STOPPED MAIN server
lifecycle.level_load CL_EV_LIFECYCLE_LEVEL_LOAD MAIN server
lifecycle.level_save CL_EV_LIFECYCLE_LEVEL_SAVE TICK server
lifecycle.level_unload CL_EV_LIFECYCLE_LEVEL_UNLOAD MAIN server
lifecycle.mod_reload CL_EV_LIFECYCLE_MOD_RELOAD MAIN both
lifecycle.shutdown CL_EV_LIFECYCLE_SHUTDOWN MAIN both

In singleplayer the server events repeat every time a world is opened.

C++ clight::Mod

namespace clight {
class Mod {
public:
    virtual ~Mod() = default;
    virtual void on_construct() {}          // the mod was loaded: subscribe here
    virtual void on_register() {}           // lifecycle.register
    virtual void on_registries_frozen() {}
    virtual void on_common_setup() {}
    virtual void on_client_setup() {}
    virtual void on_server_setup() {}
    virtual void on_server_starting() {}
    virtual void on_server_stopped() {}
    virtual void on_unload() {}             // at shutdown; then every lambda the wrapper keeps is freed
};
}
#define CLIGHT_MOD(Class)   /* defines the exports and constructs one Class */

The object lives inside your module, so no C++ class crosses the ABI. Put CLIGHT_MOD in exactly one .cpp file.

Lua

The entry script runs during construct. Lifecycle hooks are ordinary events: cl.on("lifecycle.client_setup", fn).