Players
Status: Partly implemented (API 1.11). Name, messages and the action bar, titles, give, inventory get / set / take / count / shrink / consume (nothing used up in creative, like
ItemStack#consume), game mode, selected slot, the online player list, broadcasts, the local client player, plus every entity function. API 1.6 kicks; API 1.11 adds the camera, movement modifiers, model parts and part models, animation layers, disabled hands, locked slots, input overrides, food, saturation, exhaustion, air and natural regeneration (Guide: Player Control). Abilities, experience, permission checks, respawn points, stats, advancements and the tab list are Planned.
Function table: entity (the cl_player_* and cl_server_* functions live in the entity module). Players are entities (cl_entity): every cl_entity_* and cl_living_* function applies. The cl_player_* functions return CL_E_WRONG_TYPE (or a default) for other entities.
Server-side functions
| Group | C | Lua | C++ |
|---|---|---|---|
| Name | cl_player_name(p, buf, cap, &n), C cl_easy_player_name(p, buf, cap) |
p.name |
p.name() ("" on error), p.try_name() |
| Chat | cl_player_send_message(p, text), cl_easy_send_message(p, "Hi"), cl_easy_send_messagef(p, "%d", n) |
p:send_message("Hi") (any value, or a text) |
p.send_message("Hi") |
| Action bar | cl_player_send_action_bar(p, text), cl_easy_send_action_bar |
p:send_action_bar("Hi") |
p.send_action_bar("Hi") |
| Title | cl_player_send_title(p, title, subtitle, fade_in, stay, fade_out) (-1 keeps a time; null subtitle keeps the current one), cl_player_clear_title(p, reset_times), cl_easy_send_title(p, "Title", "Sub") |
p:send_title("Title", "Sub", { fade_in = 10, stay = 70, fade_out = 20 }), p:clear_title() |
p.send_title("Title", "Sub"), p.clear_title() |
| Give | cl_player_give(p, stack) (what doesn’t fit is dropped), cl_easy_give(p, "minecraft:apple", 3) |
p:give("minecraft:apple", 3) |
p.give("minecraft:apple", 3) |
| Take | cl_player_take(p, CL_STR(id), n) → removed (slots 0-40 in order, like /clear; every game mode) |
p:take(id, n) |
p.take(id, n) |
| Count | cl_player_count_item(p, CL_STR(id)) |
p:count_item(id), p:has_item(id, n) |
p.count_item(id) |
| Slots | cl_player_inventory_get(p, slot) (owned copy), cl_player_inventory_set(p, slot, stack), cl_player_inventory_shrink(p, slot, n), cl_player_inventory_consume(p, slot, n) |
p:slot(n) / p:held_item("main" or "off"): a live slot with .id, .count, .empty, :shrink(n) (consume), :remove(n), :set(id, n), :clear(), :stack() |
p.inventory_get(slot), inventory_set, inventory_shrink, inventory_consume, p.consume_held(n) |
| Selected slot | cl_player_selected_slot(p) (0-8) |
p:selected_slot() |
p.selected_slot() |
| Game mode | cl_player_game_mode(p) → CL_GAMEMODE_SURVIVAL, … |
p.gamemode ("survival", "creative", "adventure", "spectator") |
p.game_mode() |
Slots use vanilla numbers: 0-8 hotbar, 9-35 main inventory, 36-39 armour, 40 offhand. inventory_set with the same item already in the slot only changes the count (name, enchantments and damage stay); an empty stack clears the slot. inventory_consume removes nothing in creative mode; inventory_shrink and take always remove.
All of these run on the server tick (TICK).
Player control (API 1.11)
| Group | C | Lua | Side |
|---|---|---|---|
| Movement modifiers | cl_player_add_movement_modifier(p, &m) (cl_movement_modifier: id, speed_mult, jump_mult, step_height, can_sprint, can_jump, sway), cl_player_remove_movement_modifier(p, id), cl_player_has_movement_modifier, cl_player_movement(p, &state) |
p:add_movement_modifier{ id = "broken_leg", speed_mult = 0.6, can_sprint = false }, p:remove_movement_modifier(id), p:movement() |
server (movement: both) |
| Model parts | cl_player_set_part_visible(p, CL_PART_LEFT_ARM, false), cl_player_part_visible, cl_player_set_part_model(p, part, path, &opts) (cl_part_model_opts: scale, offset, rotation, keep_part, clip) |
p:set_part_visible("left_arm", false), p:set_part_model("right_leg", "my_mod:models/peg_leg.glb", { scale = 1 }) (nil path: the vanilla part) |
both (server: synced) |
| Animation layers | cl_render_player_animations(path, &model) once, then the cl_entity_anim_* functions on players |
cl.render.player_animations(path), p:anim_play("limp", { layer = 1 }) |
both (server: synced) |
| Hands | cl_player_set_hand_disabled(p, CL_HAND_OFF, true), cl_player_hand_disabled |
p:set_hand_disabled("off_hand", true), p:hand_disabled("off_hand") |
server (query: both) |
| Slots | cl_player_lock_slot(p, slot, locked, select_lock), cl_player_slot_locked |
p:lock_slot(40), p:lock_slot(0, true, true), p:unlock_slot(0) |
server (query: both) |
| Input | cl_player_set_input_override(p, &in) (cl_input_override: forward … sprint, each -1 / 0 / 1), cl_player_clear_input_override, cl_player_input_override |
p:set_input_override{ crouch = true, sprint = false }, p:input_override(), p:clear_input_override() |
both |
| Food | cl_player_food, cl_player_set_food (0…20), cl_player_saturation, cl_player_set_saturation, cl_player_exhaustion, cl_player_exhaust(p, amount) |
p:food(), p:set_food(6), p:saturation(), p:exhaust(2) |
server (reads: both for the local player) |
| Air | cl_entity_air(e), cl_entity_max_air(e), cl_entity_set_air(e, ticks) (every living entity) |
e:air(), e:set_air(60) |
server (reads: both for the local player) |
| Regeneration | cl_player_set_natural_regen(p, false), cl_player_natural_regen |
p:set_natural_regen(false) |
server |
| Camera | cl_camera_get(&info) (pos, forward, up, yaw, pitch, fov, first_person, detached), cl_camera_position, cl_camera_is_first_person |
cl.camera.get(), cl.camera.position(), cl.camera.is_first_person() |
client |
Speed, jump and step height are vanilla attribute modifiers (seecraft:clight/<mod>/<id>, not saved), so every client’s movement prediction applies them: no rubber-banding, vanilla clients included. Celestial clients with CLight also get the rest (no sprinting, sway, hidden parts and models, animation layers, locks, forced keys) through clight:entities; vanilla clients get nothing and the server enforces what it can (it refuses locked slots and disabled hands, keeps a vanilla client’s crouch held and sprint released in its own copy, takes away the sprint bonus). Modifiers, parts and locks last until removed or the player leaves; they survive death. Function table: entity, camera.
The player list
cl_result cl_server_players(cl_entity* out, size_t cap, size_t* out_count); // online players
cl_result cl_server_broadcast(cl_text message); // a chat line to everyone
| C | Lua | C++ |
|---|---|---|
cl_server_players(buf, cap, &n) |
cl.entity.server_players() → a list |
clight::entity::server_players() → Expected<std::vector<Entity>> |
cl_server_broadcast(text), cl_easy_broadcast("Hi") |
cl.entity.server_broadcast("Hi") |
clight::broadcast("Hi") |
Client side
| C | Description | Context |
|---|---|---|
cl_entity cl_client_player(void) |
The local player (null when not in a world) | ANY |
cl_level cl_client_level(void) |
The client level | ANY |
cl_client_show_message(text, overlay) |
A chat line (or action bar) only this client sees | client TICK |
cl_client_show_title(title, subtitle, fade_in, stay, fade_out) |
A title only this client sees | client TICK |
Lua: cl.entity.client_player(), cl.client.message("Hi"), cl.client.action_bar("Hi"), cl.client.title("Hi", "sub"). C: cl_easy_show_message("Hi").
Player events
| Event | Cancellable | Mutable fields |
|---|---|---|
player.join |
no | |
player.leave |
no | |
player.respawn |
no | |
player.chat |
yes | message |
player.command |
yes | command |
player.interact_block |
yes (and a result) | |
player.interact_item |
yes (and a result) | |
player.attack_entity |
yes |
Payload fields: generated event list.
cl.on("player.join", function(event)
local p = event.player
if p:count_item("minecraft:bread") == 0 then p:give("minecraft:bread", 3) end
p:send_title("Welcome", p.name)
end)
Planned
Abilities (fly, speeds), experience, permission checks, transfers, respawn points, stats and advancements, cooldowns, the tab list, per-viewer visibility, the player’s locale and client brand, and more player events (login, move, game mode change, container clicks).