Skip to content

Inventories and menus

Status: Partly implemented. Server container menus: a vanilla menu type (CL_MENU_CHEST_9X1 … 9X6, CL_MENU_DISPENSER_3X3, CL_MENU_HOPPER) over a container your mod owns, so vanilla clients use it normally. Slots hold an item and a count (other components are not copied). Player inventories: Players. API 1.11: the slots of any block container in the world (cl_level_container_size / _get / _set, Lua level:container(pos); full item data) and locked player inventory slots (cl_player_lock_slot). Custom menu types with their own layout and screen, data slots and container events are Planned.

Function table: menu.

Opening a menu

typedef struct cl_menu_desc {
    uint32_t     struct_size;
    cl_menu_kind kind;       // CL_MENU_CHEST_9X3 by default
    cl_text      title;      // borrowed for the call
    bool         locked;     // true (default): every click is cancelled unless on_click returns true
    cl_menu_click_fn on_click;   // (menu, player, slot, button, click_type, user) -> true = let vanilla handle it
    cl_menu_close_fn on_close;   // (menu, player, user)
    void*        user;
} cl_menu_desc;
/* CL_MENU_DESC_INIT */

cl_result     cl_menu_open(cl_entity player, const cl_menu_desc* desc, cl_menu* out);   // server, TICK
cl_result     cl_menu_close(cl_menu m);
bool          cl_menu_is_open(cl_menu m);
cl_entity     cl_menu_player(cl_menu m);
int32_t       cl_menu_size(cl_menu m);                                  // container slots (without the player inventory)
cl_result     cl_menu_set_item(cl_menu m, int32_t slot, cl_item_stack stack);   // copies item + count, syncs to the client
cl_item_stack cl_menu_get_item(cl_menu m, int32_t slot);                // owned

Slots are numbered from 0 in the container part (a 9x3 chest has 0-26); clicks in the player’s own inventory part arrive with slot numbers after those. click_type is CL_CLICK_PICKUP, QUICK_MOVE (shift), SWAP (number keys), CLONE, THROW, QUICK_CRAFT (drag) or PICKUP_ALL (double click).

Bindings

C C++ Lua
cl_menu_open(p, &d, &m) clight::menu::open(player, {.kind, .title = "Shop", .locked, .on_click = [](Menu, Entity, int32_t slot, int32_t button, cl_click_type) { ... }, .on_close}) → Expected<Menu> (the lambdas are freed when it closes) cl.menu.open(player, { kind = "chest_9x1", title = "Shop", on_click = function(m, who, slot, button, click) ... end, on_close = fn })
cl_menu_set_item(m, i, s) m.set_item(i, s) m:set_item(i, "minecraft:diamond") (an id or a stack)
cl_menu_get_item(m, i) m.get_item(i) → ItemStack m:get_item(i)
cl_menu_close(m) m.close() m:close()

Lua on_click returns true to let vanilla handle the click (take or place the item); return nothing to cancel it.

Example: a shop (Lua)

local function open_shop(player)
  local shop = cl.menu.open(player, {
    kind = "chest_9x1",
    title = "Ruby shop",
    on_click = function(menu, who, slot)
      if slot == 0 and who:take("my_mod:ruby", 1) == 1 then
        who:give("minecraft:diamond")
        who:send_action_bar("Thanks!")
      end
    end,
  })
  shop:set_item(0, "minecraft:diamond")
end

cl.on("server.command_register", function()
  cl.cmd.register(cl.cmd.literal("shop"):executes(function(ctx)
    local p = ctx:player()
    if p then open_shop(p) end
  end))
end)

The complete version is /rubylua shop in clight-sdk/examples/ruby_lua.

Planned

Generic containers (get, set, add, count) for chests and other blocks, temporary containers, slot filters, more vanilla menu kinds (anvil, crafting, furnace, merchant, …), custom menu types with a fallback for vanilla clients and an RmlUi screen with slot elements, data slots for progress bars, the carried stack, item components in menu slots, and the container open, close and click events.