Skip to content

Keybinds

Status: Implemented. Keybinds show in Options → Controls, can be rebound to keys and mouse buttons and save in options.txt. FOV, mouse and camera events (for a smooth zoom) are Planned.

Goal: a Dash key that asks the server to push the player forward, and a Ruby info key that shows a client-side message. Both appear in Controls under “Ruby Tools”.

Language: Lua, with C++ at the end. APIs: Input and keybinds, Networking.

1. Register the keybinds

local dash, info
cl.on("lifecycle.client_setup", function()
  dash = cl.keybind.register{ id = "my_mod:dash", default_key = "key.keyboard.r", category = "Ruby Tools", label = "Dash" }
  info = cl.keybind.register{ id = "my_mod:info", default_key = "key.mouse.4", category = "Ruby Tools", label = "Ruby info" }
end)
  • The option name is key.<ns>.<path> (key.my_mod.dash). A lang entry with that key names it; otherwise label does.
  • category is a lang key or plain text; empty means your mod’s name.
  • default_key is a vanilla key name: key.keyboard.r, key.keyboard.left.shift, key.mouse.4, key.keyboard.unknown (unbound).

2. Presses: consume_click on the client tick

cl.on("tick.client_post", function()
  if not dash then return end
  while dash:consume_click() do
    cl.net.send_to_server("my_mod:dash", cl.buf.create(0))
  end
  while info:consume_click() do
    local p = cl.entity.client_player()
    if p then cl.client.message("You carry " .. p:count_item("my_mod:ruby") .. " rubies (" .. info:key() .. ")") end
  end
end)

consume_click returns true once per press, including presses that happened between ticks, like vanilla KeyMapping. is_down() tells whether the key is held right now.

3. The server side of the dash

The client never moves itself: it asks, and the server decides.

local last = {}
cl.on("lifecycle.register", function()
  cl.net.payload_register{ id = "my_mod:dash", dir = "to_server", max_size = 8, handler = function(sender)
    local now = sender.level:game_time()
    if last[sender.uuid] and now - last[sender.uuid] < 40 then return end   -- 2 seconds cooldown
    last[sender.uuid] = now
    local p = sender.pos
    sender:teleport(cl.vec(p.x, p.y + 0.5, p.z))   -- motion APIs are planned; a small hop for now
  end }
end)

4. C++

void on_client_setup() override {
    clight::keybind::add("my_mod:dash", "key.keyboard.r", [] {
        auto b = clight::net::buf_create(0);
        clight::net::send_to_server("my_mod:dash", b);
    }, {.category = "Ruby Tools", .label = "Dash"});
}

The function runs once per press for the mod’s lifetime.

5. Raw key events

When a keybind isn’t enough (a key combination inside your own screen), subscribe to client.key; cancel it to consume the key. Prefer keybinds: players can rebind them.

Planned

Keybinds with modifier keys and toggle mode, mouse and scroll events, FOV and camera events for a smooth zoom, and setting the player’s motion from the server.