Skip to content

Changelog

Status: implemented (v0.1).

API 1.24 (2026-10-28): animation helpers

  • Animations (VX1): cl_ui_ease (enum cl_ui_easing: CL_EASE_SMOOTH, GENTLE, SNAPPY, BOUNCY, LINEAR), cl_ui_anim_step, cl_ui_anim_enabled, cl_ui_anim_duration; Lua cl.ui.ease / anim_step / anim_enabled / anim_duration. The same curves as the client’s own HUD and menu animations, which the player turns on in Features > Animations (off by default). The UI Kit.

API 1.23 (2026-10-28): Features and the colour picker

  • Features: cl_ui_feature_register (cl_ui_feature_desc: id, name, description, icon, cl_ui_feature_category, keywords, option, page) and cl_ui_feature_remove; Lua cl.ui.feature_register{...}. Mod features show as cards in the client’s Features screen (switch, pin, search over names, keywords and the rows of their page). Appearance Pages.
  • Colour picker: cl_widget_color_picker_alpha (Lua w:color_picker_alpha(on)) adds an opacity bar. In the Fancy style the kit’s picker and cl_ui_page_color rows are the client’s colour picker (wheel, brightness, opacity, hex typing, theme colour, presets, recent and saved colours); "#RRGGBBAA" options keep their opacity. The UI Kit.

API 1.22 (2026-10-27): React-style components, HTML/CSS overlays and panels

  • Components (the view module): cl_view_open / cl_view_mount with cl_view_desc (render, on_event, on_effect, on_unmount), cl_view_begin / cl_view_end / cl_view_node / cl_view_component, props cl_view_prop_num / cl_view_prop_str and cl_view_get_num / cl_view_get_str, named state cl_view_state_* / cl_view_set_state_*, cl_view_invalidate, cl_view_effect, cl_view_get_stats, cl_view_flush, cl_view_close / cl_view_is_open / cl_view_root, enum cl_view_kind. The engine keeps the tree, matches nodes by key or position, applies only changed props to the kit widgets, memoises child components with equal props and renders nothing while state is unchanged. Lua: cl.ui.component, cl.ui.open / mount, cl.ui.use_state / use_effect / use_ref / use_memo / use_callback, node constructors (cl.ui.column{...}, cl.ui.button{...}, …), cl.ui.map. C++: clight_ui.hpp. Components.
  • RmlUi documents: cl_rml_desc gains parent (a panel in the mod’s own open screen), anchor / x / y / width / height / id / label (a HUD overlay, placed like a HUD element, never taking input), model and actions with on_action; cl_rml_data_set_num / set_str / set_bool / set_json, cl_rml_data_get_json, cl_rml_shown. The theme sheet celestial:theme.rcss and the cl-fancy / cl-vanilla body classes follow Appearance. Lua: doc:set(name, value) / doc:get(name), model / actions as lists, anchors by name, actions with Lua arguments. HTML/CSS Overlays and Panels.
  • Sandbox for documents: documents, linked sheets and templates, and markup / attributes / properties set by code are checked; URLs, absolute paths, .., percent-encoding, <script>, event attributes, <iframe> / <object> / <embed>, @import and other models are refused (CL_E_PERMISSION_DENIED); while a document loads only mods/<id>/ is readable. Document screens show the mod’s name.
  • cl.has("rml.overlay"), cl.has("rml.data_model") and cl.has("ui.components") are true.
  • Example mods: sdk/examples/react_settings_lua (Waypoint Settings), sdk/examples/html_hud_lua (Mana HUD).

API 1.21 (2026-10-25): the UI kit, theme tokens, HUD elements, Appearance pages

  • Themed screens: cl_screen_desc.themed; the screen and its widgets follow the player’s Appearance style (Vanilla / Fancy) and theme, live. The UI Kit.
  • Kit widgets: cl_widget_toggle, cl_widget_icon_button, cl_widget_dropdown, cl_widget_tabs, cl_widget_list (+ cl_widget_list_add / cl_widget_list_clear), cl_widget_panel, cl_widget_card, cl_widget_heading, cl_widget_color_picker; Lua s:toggle(...) and so on.
  • Themed drawing: cl_gui_panel, cl_gui_hud_panel, cl_gui_round_rect, cl_gui_ui_text, cl_gui_icon, cl_gui_control.
  • Theme tokens (read-only): cl_ui_fancy, cl_ui_hud_fancy, cl_ui_theme, cl_ui_color, cl_ui_length, cl_ui_text_width, cl_ui_line_height, cl_ui_generation, cl_ui_has_icon. Theme Tokens.
  • HUD elements: cl_hud_element_register (cl_hud_element_desc: id, label, icon, size, anchor, panel, cached, render, visible), cl_hud_element_remove / set_size / invalidate / shown; they appear in the HUD editor and Appearance > HUD, placements are saved. HUD Elements.
  • Appearance pages: cl_ui_page_register and rows cl_ui_page_group / note / option / choice / color / button / hud_element, cl_ui_page_open / remove; Lua cl.ui.page_register{...}. Appearance Pages.
  • Manifest: "screenshots" and "categories" (the Mods screen shows them).
  • Mods screen: the Mod Library (Fancy) with search, filters, details, permissions and the player’s on/off switch per mod (config/clight/mods.json, applies at the next start); config/clight/updates.json (written by the launcher) shows available updates.
  • Example mod: sdk/examples/ui_kit_lua (Compass Plus).

API 1.20 (2026-10-24): files, HTTP, typed attachments, equipment, entity events, emitters, WebAssembly hot reload

  • Files (C10): cl_fs_read / read_text / write / write_text / append_text / exists / stat / list / mkdir / remove / rename / data_used / data_quota; Lua cl.fs (returns nil, message like io), cl.fs.read_json / write_json, cl.json.encode / decode. Reads reach the mod’s folder, writes only data/, atomic, 256 MiB quota, links and junctions never followed. Filesystem and HTTP.
  • HTTP (C10): cl_http_request with cl_http_opts (method, body, headers, timeout, size limit, save_to), cl_http_cancel, cl_http_error, cl_http_header, cl_http_allowed; Lua cl.http.get / post / request. Only hosts covered by a declared and allowed net.http:<host> (also *.<domain>), https only, redirects checked, no cookies, answers at the next tick on the side that asked.
  • Capabilities from code: cl_cap_granted, cl_cap_request (opens the Permissions screen), event mod.permission_changed; Lua cl.permission, cl.request_permission.
  • Typed attachments: cl_attachment_register with cl_attachment_desc (default_snbt, CL_ATTACH_SYNC, CL_ATTACH_COPY_ON_DEATH); Lua cl.attachment(key, { default, sync, copy_on_death }). Data Attachments.
  • Equipment (C11): cl_entity_equipment, cl_entity_set_equipment, cl_entity_set_drop_chance, enum cl_equip_slot.
  • Events (C11): entity.spawn (cancellable, loaded), player.interact_entity (cancellable, result), entity.change_dimension.
  • Particle emitters (C11): cl_render_emitter_add (on an entity or a bone, every N ticks), cl_render_emitter_remove, cl_render_emitter_set_active; Lua e:emit(type, opts).
  • WebAssembly hot reload (C11): in dev mode a rebuilt .wasm replaces the running module; CLIGHT_MOD_HOT_STATE(var) / CLIGHT_MOD_SAVE_STATE(fn) / CLIGHT_MOD_RESTORE_STATE(fn) hand state over; a failing build rolls back. cl.has("hot_reload.wasm"), cl.has("fs"), cl.has("net.http") are true. Hot Reload.
  • Lua errors from API calls now carry the engine’s detail (cl.fs_mkdir failed: permission_denied: fs_mkdir: path not allowed: ...).
  • Fixes: e:set_casts_shadow (API 1.18) was not installed on entity handles in Lua; falling blocks cache their lighting per block position and light (the same picture, less work per frame).

API 1.19 (2026-10-24): the version layer

  • The compatibility promise: a mod built against any API 1.x keeps loading and behaving the same on every later API 1 Celestial (ABI stability and API versioning). Golden mods built once against API 1.0, 1.4, 1.6 and 1.11 (Lua, C and C++) run in CI on every build.
  • Stable names with a per-build map (idl/generated/version_map.json); compat.idl renames for events, accessors and cl.has features; deprecated functions forward to their replacement (@deprecated @forward) with a one-time CLL-2010 warning per mod.
  • Renamed vanilla ids keep working in the API’s lookups, per registry: minecraft:grass / short_grass, chain / iron_chain, scute / turtle_scute, grass_path / dirt_path, zombie_pigman / zombified_piglin and the 1.20 pottery sherds.
  • Manifest: "api_version" (a minimum minor, ">=1.12" or "1.12") with clear CLL-2003 messages; "former_ids" and "id_aliases" for renamed mods and content.
  • Lua: older spellings (cl.net.buf_write_i32, cl.item_stack, …) warn once per mod; the API 1.0 method calls player:name(), player:health(), player:uuid() and player:level() work again (API 1.2’s properties had shadowed them).

API 1.18 (2026-10-24): entity lighting and shader pack entity shadows

  • Without a shader pack: falling blocks and piston-moved blocks are lit like terrain (per-corner light and ambient occlusion, like vanilla), entities in the Nether use the Nether’s light directions, armor stand markers and block entities take their light like vanilla.
  • With a shader pack: entities, block entities and mod models cast shadows into the pack’s shadow map (also off screen and the player’s own body in first person); Video Settings > Shader Entity Shadows (on by default).
  • New: cl_render_set_type_casts_shadow, cl_render_type_casts_shadow, cl_render_entity_shadows_setting, cl_render_entity_shadows_active, cl_entity_set_casts_shadow, cl_entity_casts_shadow, enum cl_shadow_mode; Lua e:set_casts_shadow(true | false | nil). cl.has("render.entity_shadows"). Guide: Entity Lighting and Shadows.

API 1.17 (2026-10-24): world and data mixin targets

  • 18 more mixin targets (C7): world generation (chunk.generate.stage, worldgen.structure.start, worldgen.biome.fill with a point per biome cell, worldgen.terrain.build with skip points before the noise fill, the surface and the carvers, worldgen.feature.place, worldgen.structure.place; WebAssembly mods only), chunks (chunk.load, chunk.unload, chunk.save), commands (command.execute, command.register, command.suggest), recipes, loot and tags (recipe.match, recipe.assemble, loot.table.roll, tag.contains) and data (data.reload, data.registry_freeze). See the mixin target appendix and Mixins.

API 1.16 (2026-10-24): client mixin targets

  • 19 client mixin targets (C6) and the local player’s prediction variants; see the mixin target appendix.

API 1.15 (2026-10-24): item and block models, LOD

  • Item models: cl_render_item_model / cl.render.item_model(item, path, opts) draws an item with a glTF model in the hand (first and third person), the GUI, item frames, on the ground, on heads and shelves; vanilla item display transforms (JSON or a Lua table; left-out contexts use the block item defaults), fit to a block, rotation, offset, a looping default_clip, the enchantment glint. Guide: Item and Block Models.
  • Block models: cl_render_block_model / cl.render.block_model(block, path, opts) for block entities (instead of the vanilla renderer, or with keep_vanilla) and any other block (on top of its block model); the model turns with facing / rotation.
  • LOD: MSFT_lod (with MSFT_screencoverage) and _lodN node / mesh names; cl_model_set_lod_distances, cl_model_lod_levels.
  • Materials: the glTF alpha mode and cutoff are honoured (also in shader packs); normal and metallic-roughness maps reach shader packs as LabPBR normals / specular.
  • cl.has: item.models, block.models, block_entity.models (now true), model.lod, model.alpha_mode, model.pbr_shaderpacks.

API 1.14 (2026-10-24): mixin targets

  • 15 more game-logic mixin targets (C5b); see the mixin target appendix.

API 1.13 (2026-10-23): rendering extras and menus

  • Model looks: glTF KHR_materials_variants (cl_model_variants, cl_entity_set_material_variant / cl_entity_material_variant), per-entity render parameters (cl_entity_set_render_param, cl_entity_render_param, cl_entity_clear_render_param; synced numbers from the server, local values on a client) and custom fragment shaders per model (cl_model_set_shader: mod GLSL with cl_shade(), checked and compiled by the engine, up to 8 parameters and 4 textures). Guide: Post-processing, Fog, Sky and Camera.
  • Particles and decals: mod particle types from sprite sheets (cl_render_particle_type), cl_level_particle on both sides (vanilla clients get the type’s vanilla fallback), decals clipped to block faces (cl_level_decal, cl_level_clear_decals). Payload channel clight:fx. Guide: Particles and Decals.
  • Post-processing: cl_render_post_pass (mod GLSL cl_post(uv) over the world and the hand, up to 16 uniforms, 4 textures), cl_post_pass_set_uniform / set_enabled / is_enabled / remove; shader packs get uniform float <modid>_<name> (cl_render_set_uniform) and #define MOD_<ID>.
  • Fog, sky, light: event render.fog (mutable), event render.frame, cl_render_set_sky_color, cl_render_set_lightmap_tint, cl_render_set_ambient_darkness, cl_render_clear_overrides.
  • Camera effects: cl_camera_set_effects / clear_effects / get_effects (FOV multiplier, roll, view-bob scale, vignette, shake only with the player’s new Mod Camera Shake option), following FOV Effects, Distortion Effects and View Bobbing.
  • Menus: cl_gui_set_title_background, cl_gui_set_panorama, cl_gui_set_sprite (per screen or global, keeps the sprite’s nine-slice scaling), cl_gui_clear_sprites; Lua also cl.menu.*. Guide: Menus and Themes.
  • AI: cl_entity_path_to_entity (re-plans when the target moves).
  • Lua: level:particle(type, pos, vel, count, spread), level:decal(texture, pos, normal, size, rotation, lifetime), colours as "#RRGGBB", lists for comma-separated fields, cl.render.post_pass{ uniforms = function(pass, frame) ... end }, model:variant_list().
  • cl.has: entity.render_params, model.variants, model.shaders, render.particles, render.decals, render.post, render.pack_uniforms, render.fog, render.sky, render.lightmap, camera.effects, camera.shake, gui.title_background, gui.panorama, gui.theme, ai.follow_entity.

API 1.12 (2026-10-23): AI, navigation and saved data

  • Paths: cl_level_find_path / level:find_path(from, to, opts) plans without moving (an owned cl_path: cl_path_get_info, cl_path_nodes), cl_entity_path_to / e:path_to(pos, speed, opts) walks, plus cl_entity_follow_path, cl_entity_path, cl_entity_path_state, cl_entity_path_done, cl_entity_path_stop. Options: the body (default: the entity’s current hitbox), the node evaluator, doors (walking through adds vanilla’s OpenDoorGoal), climbing #climbable blocks, reach, the node budget, the drop height, the water malus and named path costs (cl_ai_path_cost_register; Lua avoid = function(pos)). The vanilla path finder with the mob’s own options; vanilla navigation is unchanged. Guide: AI and Pathfinding.
  • Looking and targets: cl_entity_look_at, cl_entity_look_at_entity, cl_ai_set_target, cl_ai_target.
  • Goals: goal types (cl_ai_goal_type_register: can use, can continue, start, tick, stop, removed, flags, target selector, interruptable, every tick), cl_ai_add_goal on mod mobs and vanilla mobs (brain mobs too), cl_ai_type_goal for every mob of a type (also after loading), cl_ai_remove_goal, cl_ai_goal_running, cl_ai_goal_entity, cl_ai_goal_type, cl_ai_clear_goals. Lua: cl.ai.add_goal(e, priority, { can_use = fn, ... , state = {} }). Goal list changes apply at the end of the tick.
  • Data attachments: cl_entity_data_* on every entity and player (saved as clight:data, players in their file; CL_DATA_SYNC sends a value to Celestial clients with CLight, event entity.data_changed), cl_level_chunk_data_* on loaded chunks (saved in the chunk), cl_level_saved_data_* per level (the world’s data/<namespace>/<path>.dat). Values as SNBT text or binary NBT; Lua: plain Lua values, e:data / e:set_data, level:chunk_at(pos):data, level:saved_data(key) (a live table written at every save). Vanilla ignores all of it; data of removed mods stays. Guide: Saved Data.
  • C++: clight::Path is an RAII handle.
  • cl.has: ai.paths, ai.find_path, ai.path_doors, ai.path_climb, ai.path_cost, ai.look_at, ai.goals, ai.type_goals, ai.target, data.entity, data.player, data.chunk, data.saved, data.sync, data.nbt.

API 1.11 (2026-10-23): world access and player control

  • Ray casts: cl_level_raycast / level:raycast(from, to, opts) with collision, outline, visual or no block shapes, fluids (none, source, any, water), entity hitboxes (margin, ignore, source entity), on the server and the client. Guide: World Access.
  • Bulk reads and snapshots: cl_level_get_blocks (up to 128³ states in one call, x / z / y order, CL_BLOCK_UNLOADED), cl_level_snapshot (an owned copy readable from any thread; cl_snapshot_get / copy / get_info).
  • Block states and facts: cl_block_state_property / _with / _properties / _property_values / _to_string / _parse, cl_level_set_block_property, cl_block_get_info (shape facts, hardness, friction, light, map colour), cl_block_sound_type ("stone", "wood", …) and cl_block_sound_event. Lua: typed values (cl.registry.state_property(s, "open") → true), "minecraft:x[prop=value]" wherever a state goes.
  • Light and height: cl_level_light (block, sky, combined, sky darkening), cl_level_can_see_sky, cl_level_heightmap.
  • Containers and block entities (server): cl_level_container_size / _get / _set (every data component kept), cl_level_block_entity_type / _get / _merge (SNBT, like /data). Item stacks taken from the world keep all their components in player_give, player_inventory_set and player_inventory_get.
  • Event level.block_changed on both sides (server: end of the level tick, in order; client: right after the change; filter by the new block; free without subscribers).
  • Players: cl.camera (client), stackable movement modifiers through vanilla attribute modifiers (speed, jump, step height, no sprinting, no jumping, camera sway), model part visibility and glTF part models, animation layers on the vanilla player model (cl_render_player_animations), disabled hands, locked slots, input overrides, food / saturation / exhaustion, air (cl_entity_air) and per-player natural regeneration. Synced to Celestial clients through clight:entities ops 3 and 4; vanilla clients get nothing and the server enforces locks itself. Guide: Player Control.
  • Lua: array results are no longer cut at 256 entries; the prelude loads faster.
  • cl.has: world.raycast, world.bulk_reads, world.snapshots, world.light, world.heightmaps, block.properties, block.info, block.sound_type, block.containers, block_entity.data, level.block_changed, camera, player.movement_modifiers, player.model_parts, player.part_models, player.animations, player.hand_locks, player.slot_locks, player.input_override, player.food, entity.air, player.natural_regen.

API 1.10 (2026-10-23): entity gameplay

  • Queries: cl_level_entities_in_box (type, #type tag, entity tag, category, exclude, limit, nearest first), cl_level_nearest_player, cl_level_entity_by_uuid, cl_entity_by_uuid, cl_entity_category, entity tags (cl_entity_has_tag / add_tag / remove_tag). Lua: level:entities_near(pos, radius, query), boolean flags, nil when nothing is found. Guide: Entity Gameplay.
  • Attributes and effects: value / base / set base, stackable modifiers by id; effects add / remove / has / info / clear.
  • Synced data (cl_entity_set_synced_*, cl_entity_synced_*, event entity.synced on clients): only clients that registered clight:entities (Celestial clients with CLight) receive it; saved with the entity.
  • Combat: cl_entity_hurt with damage types, attacker, direct entity, position and a knockback multiplier; cl_entity_knockback; the cancellable entity.knockback event; mod damage types (cl_damage_type_register, cl.damage_types.register) with vanilla-style death messages and an English fallback, damage type tags, a vanilla fallback for the damage event packet.
  • Natural spawning: cl_spawn_rule_register / cl.registry.spawn_rule (biomes and tags, category, weight, packs, placement, base check, light, player distance, cap, condition callback) joins the biome spawn lists like a data pack entry.
  • Riding: cl_entity_set_passenger (force, bone seats on the vehicle’s glTF model), dismount, eject_passengers, vehicle, passengers.
  • Lua commands: ctx:level(). Manifests: limits.callback_ms (1-500, default 50) sets the Lua per-callback time limit.
  • cl.has: entity.queries, entity.tags, entity.attributes, entity.effects, entity.synced, entity.combat, entity.knockback, damage_type.custom, spawn.rules, entity.riding, entity.bone_seats.

API 1.9 (2026-10-23): entity models and animation

  • glTF 2.0 models for entity types (cl_render_entity_model, cl.render.entity_model): skinned meshes with 4 weights, morph targets, PBR-lite materials (base colour, normal, roughness), linear mipmapped textures, vanilla light, overlays, outlines, shadows and shader packs; mod types draw the model instead of their fallback on Celestial clients with the mod, vanilla types can be restyled client-side. Sandboxed loading from the mod’s assets; clip sidecar <model>_clips.json. Guide: Entity Models and Animation.
  • Animation (cl_entity_anim_*): clips on 8 layers with cross-fades, speed, weights, bone masks, additive layers, one-shots that fade out or hold, morph weights (cl_entity_set_morph), event markers (entity.anim_event, server and client), root motion, bone look-at with limits, bone transforms. The server’s state is synced to Celestial clients (clight:anim), late joiners included.
  • cl.has: entity.models, entity.animation, entity.anim_layers, entity.anim_events, entity.root_motion, entity.look_at, entity.bone_transform, entity.anim_sync, model.gltf, model.skinning, model.morphs, model.pbr.
  • Mod mobs no longer burn in daylight because of a fallback in the burn_in_daylight tag.

API 1.5 (2026-10-18): the first game mixin targets

  • 41 targets for game logic on the server: entity damage and death (entity.living.hurt, actually_hurt, damage_after_armor, damage_after_magic, heal, check_totem, die, knockback, cause_fall_damage), effects, ticks, movement and AI (entity.living.add_effect, tick_effects, tick, travel, jump_power, jump, entity.mob.ai_step, check_despawn, finalize_spawn, do_hurt_target, xp_reward, pick_up_items, spawn.check_rules), players (player.attack, attack_damage, interact_entity, food_tick, exhaust, give_xp, respawn, teleport, destroy_block), blocks and the level (block.player_destroy, place_item, use_without_item, use_with_item, random_tick, level.set_block, level.explode) and items (item.use, use_on, finish_using), with their points and values. The full list is Mixin targets; the frame struct cl_damage_source is new.
  • Invokers: entity.living.hurt, entity.living.heal, entity.living.knockback, player.exhaust, player.give_xp (cl.mixin.invoke, cl_mixin_invoke).
  • /clight mixins [<target> | conflicts | stats] shows the ordered mixins, the conflicts and the call statistics; in dev mode logs/clight-mixins.txt has the full table.
  • A mod that hooks every target and passes everything through leaves the world bit-identical (tested on a deterministic server run).

C++ wrapper update (2026-10-17, no ABI change)

clight.hpp only; the C ABI, Lua and old C++ code keep working (except copying a clight::Buf, see below).

  • clight::Buf is move-only. The ABI has no retain for buffers, and a copy released the same buffer twice. Copying is now a compile error; pass Buf& / const Buf& or std::move it. ItemStack and Text copies still retain.
  • [[nodiscard]] on Subscription and mixin::Handle (and mixin::inject): dropping one, which unsubscribes at once, now warns with a message.
  • Lambdas with captures everywhere, freed when the registration ends (unsubscribe, cancel, remove, a screen or menu closing, a one-shot that ran, mod unload): clight::after / every / after_client, clight::keybind::add(id, key, fn) and Keybind::on_press, hud::add_layer(id, fn, {.above, .below, .visible}) (returns the HudLayer), net::on_payload, screen::open(ScreenSpec) with Screen::button / checkbox / slider / text_field, menu::open(player, MenuSpec), registry::item / block with lambda behaviours. The engine only sees opaque ids, so a call queued for a removed lambda does nothing.
  • Command replies take plain strings: CmdCtx::reply, send_success(string, broadcast = false), send_failure(string); send_success(text) no longer needs the false.
  • CL_MIXIN without options no longer warns under clang -Wall -Wextra (-Wmissing-designated-field-initializers).

API 1.4 (2026-10-17): beginner fixes 2

  • Creative mode keeps items: cl_player_inventory_consume (vanilla’s ItemStack#consume: nothing is used up in creative) and cl_player_game_mode. Lua slot:shrink(n) and C++ / C consume_held use it; Lua slot:remove(n) always removes; player.gamemode ("survival", "creative", …). player:take still removes in every mode (like /clear).
  • Slot set keeps components: cl_player_inventory_set / slot:set(id, n) with the item already in the slot only changes the count (name, enchantments, damage stay).
  • block.break xp in vanilla order: the value a handler reads is a preview rolled on a copy of the level random; the game still rolls the real amount after the loot, so the random sequence is exactly vanilla’s. An unchanged xp keeps vanilla’s own roll; a changed one replaces it. (Fixes the 1.3 note above: the order no longer changes.)
  • Mod block properties are used: destroy_time, friction, speed_factor, jump_factor, light_emission, sound_type (a SoundType name like "glass" or a block id). A field left at its default keeps the value of copy_properties_from, else of the base block. The server simulates them (mobs, items, mining, light, sounds) and Celestial clients predict the same (movement, mining, light, sounds).
  • Mod block loot tables: <ns>:blocks/<path> from the mod’s data pack (26.3 format, mod items allowed), used for player breaks, explosions and /loot; without one the block drops its item.
  • Commands: requires_permission works on any node (a name = operators, or a level 0 to 4); argument types "player", "players", "entity", "entities" (names, UUIDs and selectors through the server’s EntityArgument), "string" reads "quoted phrases", selectors with spaces in [...] stay one argument. C ABI: the tree builder cl_cmd_literal / cl_cmd_argument / cl_cmd_then / cl_cmd_executes / cl_cmd_requires_level / cl_cmd_requires_permission / cl_cmd_register_tree, the getters cl_cmd_arg_str / _i32 / _f64 / _bool / _entities, cl_cmd_ctx_permission_level, cl_cmd_ctx_entities. C++: chainable literal("x").then(...).executes([](CmdCtx c) { ... }).register_command(). C: cl_easy_arg_str / _int / _entity. Lua: ctx:arg gives the entity (or a list), ctx:entities(name), ctx:permission_level().
  • Text: cl_text_set_style (bold, italic, underlined, strikethrough, obfuscated; on or explicitly off), cl_text_click (open_url, run_command, suggest_command, copy_to_clipboard), cl_text_hover, cl_text_add_arg (translation arguments). Titles: cl_player_send_title (with fade times), cl_player_clear_title. Client-only text: cl_client_show_message (chat or action bar) and cl_client_show_title. Lua: t:bold() … (chainable, also color and append), t:click(action, value), t:hover(text), cl.text.translatable(key, args...), player:send_title(title, subtitle, { fade_in, stay, fade_out }), player:clear_title(), cl.client.message / action_bar / title. C: cl_easy_send_title, cl_easy_show_message.
  • Keybinds in the Controls screen: mod keybinds are options key.<ns>.<path> under their category (or the mod’s name), rebindable to keys and mouse buttons (key.mouse.*), saved in options.txt; clicks come from the real key events. cl_keybind_desc.label (appended field) names a key without a lang entry; cl_keybind_key returns the bound key.
  • Config: cl_config_reload, cl_config_comment, the event config.reloaded (Lua config:on_reload(fn)); edited files are read again within a second, a hot reload re-reads them, /clight reload-config (operators) reloads every mod’s files. The Mods screen has a Config page made from the definitions (toggle, slider, text box; the comment is the tooltip).
  • Lua typings: clight.d.lua marks parameters the prelude fills in as optional (set_block flags, title times, …).

API 1.3 (2026-10-17): beginner fixes

  • clight run loads the project folder itself (dev mode), so saving a Lua script hot-reloads it. bin/ falls back to build/bin/; the mod’s data/ is build/run/<id>/data/. The old staged copy in build/run/<id>/ is removed.
  • set_block without flags = "all" (neighbours + clients) in Lua (level:set_block(pos, id)) and C++ (Level::set_block(pos, state)); cl_easy_set_block already did. The C ABI is unchanged (flags are explicit there).
  • Taking items: cl_player_take, cl_player_count_item, cl_player_inventory_shrink, cl_player_inventory_set (C ABI, appended). Lua: player:take(id, n), player:count_item(id), player:has_item(id, n), live slots player:slot(n) / player:held_item(hand) with .id / .count / .empty, :shrink(n), :set(id, n), :clear(), :stack(). C++: take, count_item, inventory_shrink, inventory_set, consume_held(n). C: cl_easy_take, cl_easy_count, cl_easy_consume_held.
  • Events that now fire: tick.level_pre / tick.level_post (per level, on the tick thread around the parallel level tick), lifecycle.level_load / level_save / level_unload, lifecycle.data_reloaded (/reload), lifecycle.resources_reloaded (client resource reload), lifecycle.mod_reload (after a hot reload that worked).
  • event:set_result(...) works: a result other than pass on player.interact_block / player.interact_item skips the vanilla handling.
  • Event filters work: the engine passes the filter key for block.break / block.place / player.interact_block (block id), player.interact_item (item id), entity.damage / entity.death (entity type) and client.screen_open (screen id).
  • block.break fills tool (and so event.item) and xp (the experience the block drops; writable). With a block.break handler the ore experience is rolled before the loot instead of after it (same amounts and odds; API 1.4 restores vanilla’s order).

API 1.2 (2026-10-17): the mixin core

  • Mixins replace the old “no mixins” decision (user decision 2026-10-17). Mods attach sandboxed Lua or WASM callbacks to named engine targets: head, return, named points, values and calls, wrap and overwrite, plus accessors and invokers. See Mixins. This release shipped the core; the first game targets came with API 1.5 (cl.has("mixin:<name>") tells you which exist).
  • C ABI: module mixin (cl_mixin_inject / remove / target_lookup / info, cl_mixin_cancel / set_return / call_original, cl_mixin_invoke, cl_accessor_lookup / get / set), the callback info cl_mixin_ci, and per target the generated frame structs and helpers. Static declarations: CL_MIXIN(...) / CL_MIXIN_SITE(...) (records read through the module’s clight_mixins export, which CLIGHT_MOD_ENTRY adds).
  • Lua: cl.mixin.inject(target, at, fn, opts) and the sugar head / ret / wrap / overwrite / point / value / call, h:remove(), cl.mixin.info, cl.mixin.invoke, cl.accessor(name):get/set. Runtime mixins follow hot reload; manifest handlers are re-bound by name.
  • C++: clight::mixin::inject<Traits>(clight::at::..., fn) with an RAII handle, clight::mx::Ci<T>, clight::accessor<V>.
  • Manifest: the "mixins" block (schema and clight check, which also checks the CL_MIXIN records of a built .wasm). The old "hooks" key is an error that names "mixins".
  • Conflicts: of two overwrites (or two exclusive call redirects) on one target, the mod that loads first wins; only the other mod’s mixin is off and the Mods screen and log say so (CLL-7005/7006). A failing mixin is turned off for the session, its changes dropped, and the game runs the vanilla behaviour exactly once (CLL-7009). New loader codes CLL-7001 to CLL-7012 (Error codes).
  • Cost: an unused target is one memory read and a branch; a mixin that passes everything through leaves the game bit-identical.

API 1.1 (2026-10-17): API cleanup

  • Manifest: "api": 1 (the API major, an integer) replaces the "clight" range string. Old manifests still load: the range is mapped to a major with a deprecation warning, and 0.x ranges from the v0.1 drafts mean API 1. Templates, examples and the JSON schema use "api": 1; clight check validates it.
  • Lua names mirror the C names: cl.buf.*, cl.task.*, cl.widget.*, cl.player.*, cl.living.*, cl.music.*. The old spellings (cl.net.buf_*, cl.sched.task_*, cl.screen.widget_*, cl.entity.player_*/living_*, cl.sound.music_*, cl.item_stack) are the same functions and keep working. cl.stack(id, count) creates a stack; cl.version reports the real API version.
  • New: cl_core_has(name) / cl.has(name) for optional modules, functions, runtimes and features.
  • ABI guard: the shipped function table, events, structs, callbacks and enums are snapshotted in client/clight/idl/abi/clight_abi.snapshot.json; a test fails if any entry is removed, reordered or changed.
  • clight.d.lua now annotates the handle methods and the prelude helpers (cl.on, cl.cmd, cl.pos, …).
  • The easy layer (no ABI change). Lua: player.name / uuid / pos / health / level / world properties (the shadowed getters are get_name() etc.; player:name() became player.name), player:give(id, count), send_message / send_action_bar with any value, callable cl.log(...), the event fields event.block / item / world / player, and id strings for block states, block ids and item ids. C++: player.give("id", n), send_message("text"), std::string name() (the Expected form is try_name()), pos(), level.set_block(pos, "id"), clight::item / literal / broadcast. C: the new header clight_easy.h (CL_ON, CL_EVENT, CL_LOG, cl_easy_give, cl_easy_send_message(f), cl_easy_player_name, cl_easy_set_block, …). The templates and examples use it.

CLight v0.1 (2026-10-05): first implementation

  • User decisions: no mixins (events, registries and behaviour overrides only; replaced by the mixin design for API 1.2), no C#, and every mod sandboxed: C and C++ compile to WebAssembly (Wasmtime, AOT-cached), Lua runs in a sandboxed VM; a mod sees only mods/<id>/ (read) and mods/<id>/data/ (read/write). No native DLL tier, no trust prompt; extra capabilities are allowed or denied per mod on the Permissions screen.
  • SDK in clight-sdk/ next to the game: the clight CLI (new, build, pack, check, run, setup, doctor), templates for C, C++ and Lua, the official wasi-sdk downloaded by clight setup, reproducible .clmod packages.
  • Mod assets (assets/) and data (datapack/) mount as packs mod/<id>; custom sounds and music through the sound API (cl_sound_*, cl_music_*, event client.sound_play).
  • The Mods screen lists mods with their errors; the Permissions screen per mod.

Spec v0.1 (2026-10-04): first documentation draft

  • First complete design of the CLight modding platform documentation (Mod_Docs/).
  • Languages: C (the stable C ABI clight.h), C++ (the header-only clight.hpp) and Lua 5.4 (sandboxed VM with hot reload). C and C++ compile to WebAssembly; every mod is sandboxed.
  • Runtimes: data, Lua, and WebAssembly (Wasmtime). Every mod is sandboxed; there is no native-code tier.
  • Single interface source clight.idl, from which all bindings and the reference tables are generated.
  • Thread contexts TICK / PARALLEL / RENDER / NET / ASYNC matching the engine’s job system and parallel dimension ticking (ARCHITECTURE §2).
  • Events with priorities, cancellation and results. Behaviour overrides replace bytecode mixins (no hook sites, no patching).
  • Registries (static and dynamic), vanilla fallbacks for vanilla clients, the configuration-phase handshake.
  • APIs for worldgen, rendering (baked models, instanced entity models, render stages, shaders, post effects), RmlUi screens, immediate-mode GUI, HUD layers, input, sound, config, attachments, services, permissions, server plugins, filesystem and HTTP.
  • Manifest schema 1 and the .clmod package format.
  • Migration guides for hand-porting (Fabric, Forge/NeoForge, Paper/Bukkit). Java mods are not loaded, converted or translated.

Versioning policy

  • Spec revisions (v0.x) can change anything until CLight 1.0 ships.
  • From 1.0, the API follows the rules in ABI stability and API versioning: additions in minor versions, removals only in major versions after one major version of deprecation.