Repository navigation
API Input and Controls
Lua API / Input and controls
Native hotkeys create user-configurable commands in Minecraft's Controls screen. Named input maps separate
game actions from physical keys and mouse buttons. Options → Controls opens Minecraft's key bindings by
default; the BetaMoon tab shows named actions. Input maps observe press/release transitions and can consume an
event before Minecraft performs its normal movement, inventory, GUI, or world action. Legacy
key_input and mouse_input events remain observational and cannot consume native behavior.
On this page
Registers a keyboard command and returns a hotkey handle. The command appears in Minecraft's Controls screen, and a player's saved remap is restored on later launches. Use hotkeys for global mod commands that players should configure alongside Minecraft's own keys; use an input map when an action needs context priority, held/released state, mouse input, modifiers, or native-input consumption.
| Field | Required | Meaning |
|---|---|---|
key |
yes | Namespaced key, stored canonically as namespace:hotkey/name. |
label |
yes | User-facing name shown in Controls; at most 128 characters. |
default |
yes | One unmodified keyboard binding such as key.g; key.none starts unbound. |
repeatWhileHeld |
no | Invoke on each ModLoader input tick while held; default false invokes once per press. |
local openBuilder = betamoon.input:hotkey {
key = "mymod:open_builder",
label = "Open Builder",
default = "key.b",
}
openBuilder:on(function(event)
showBuilder()
end)The callback event has key, binding, and keyCode, where binding and keyCode reflect the player's current
Controls assignment. getBinding() returns the current normalized binding, isActive() reports whether the script
registration is live, on(nil) removes the callback, and close() detaches the registration. Hotkey callbacks run on
gameplay ticks and do not consume Minecraft input; use a handled input-map action when consumption is
required. A callback error disables that callback and reports the script error without affecting other hotkeys.
Script unload detaches the callback, but the Controls entry remains for the rest of the Minecraft session. Redeclaring
the same key during hot reload reuses that entry and preserves its current remap. Changing repeatWhileHeld for a
retained key requires a Minecraft restart; the label may change immediately. A changed default is used on a fresh
process only when no saved assignment exists;
it never overwrites a player's remap.
Registers an owner-scoped map and returns an active input-map handle. key accepts namespace:name shorthand and is
stored canonically as namespace:input_map/name. Action names use lowercase letters, digits, _, and -.
| Field | Required | Meaning |
|---|---|---|
key |
yes | Namespaced input-map key. |
actions |
yes | Table from local action name to a binding string or action definition. A binding may be a string or array. |
priority |
no | Default context priority, -10000 through 10000; default 0. |
captures |
no | Native families captured by the default context. |
label, description
|
no | Bounded display text in the BetaMoon Controls tab. |
configurable |
no | Show the map in the BetaMoon Controls tab; default true. |
An action definition accepts default or bindings, plus optional label, description, and
configurable (default true). modes may contain hold and toggle; defaultMode must be one of the
allowed modes. Actions default to hold only. In toggle mode, each matching wheel event switches the action on or off.
local controls = betamoon.input:map {
key = "mymod:builder",
priority = 10,
actions = {
place = { default = "mouse.left" },
rotate = { default = "key.r", label = "Rotate preview" },
cancel = { default = "key.escape" },
zoom = { default = { "mouse.wheel_up", "mouse.wheel_down" } },
sprint = { default = "key.lcontrol", modes = { "hold", "toggle" }, defaultMode = "hold" },
},
}controls.key is the canonical key. Duplicate keys, duplicate action names, overlapping bindings, unknown fields,
invalid devices, and maps outside a Lua script raise an error.
Bindings are case-insensitive when declared and are returned in normalized lowercase form.
| Form | Examples |
|---|---|
| Keyboard |
key.r, key.escape, key.space, key.f5
|
| Named mouse buttons |
mouse.left, mouse.right, mouse.middle
|
| Numbered mouse buttons |
mouse.button1 through mouse.button32; button 1 is the left button |
| Wheel |
mouse.wheel, mouse.wheel_up, mouse.wheel_down
|
| Required modifiers |
ctrl+key.r, shift+mouse.left, ctrl+shift+key.s
|
Required modifiers may be held in addition to other modifiers. For example, ctrl+key.r still matches while Shift is
also held. mouse.wheel reports the signed raw wheel delta; directional wheel bindings match only their direction.
Sets the default context's callback for an action and returns controls. Passing nil removes it. A callback runs
only for semantic transitions, not once per render frame or repeatedly while held.
controls:on("place", function(event)
if event.phase == "pressed" then
beginPlacement()
end
return betamoon.callbackResults.handled
end)The callback receives:
| Field | Meaning |
|---|---|
map, action
|
Canonical map key and local action name. |
phase |
pressed or released. Wheel input is a pressed pulse. |
pressed, held, released
|
State snapshot for the current input cycle. A wheel pulse is pressed and released, but not held. |
amount |
1/0 for digital input, or signed raw wheel delta. |
cycle |
Monotonic input-cycle number. |
mode, active
|
Effective hold/toggle mode and logical activity. held remains strictly physical. |
activated, deactivated
|
Logical activation changes during this cycle. |
device, code
|
Physical source. Omitted for a synthesized lifecycle release. |
Return callbackResults.pass or nil to continue to lower contexts and Minecraft. handled and deny both stop
lower input contexts and consume the native event; use handled for an action your game performed and deny for an
action intentionally refused. An error disables only that context and falls back to the next context or native input.
Returns a detached { cycle, pressed, held, released, amount, mode, active, activated, deactivated } snapshot.
pressed and released last for one input cycle. held tracks physical input even in toggle mode. In toggle mode,
active flips on each new physical press or matching wheel event. A screen, focus, world, or player change clears
active state. Holding an action does not call Lua repeatedly.
local movement = controls:getState("place")
if movement.held then
updatePreview()
endReturns an array of the action's current normalized binding strings. The array may be empty when the player has unbound the action.
Adds another context for the same map and returns a context handle. Options accept priority and captures. Higher
priority runs first; at equal priority, the newest active context runs first. Each context has token, priority,
on(action, callback), isActive(), and close().
local wheel = controls:activate {
priority = 100,
captures = { "movement", "look", "world_actions" },
}
wheel:on("cancel", function()
wheel:close()
return betamoon.callbackResults.handled
end)Capture names are:
| Family | Native behavior suppressed while active |
|---|---|
actions |
Native input for physical events matching this map's actions. |
movement |
Forward/back/strafe/jump/sneak key forwarding. |
look |
Mouse-derived player rotation. |
world_actions |
Attack, use, place, and held block breaking. |
inventory |
Inventory/drop keys, hotbar number keys, and wheel slot selection. |
gui |
Keyboard and mouse events while a GUI screen is open, except the engine-owned Controls screens. |
Captures are independent: a spell wheel can suppress movement and look without blocking the inventory or every named action. Closing a context immediately reveals the next context.
Replaces the effective binding with a string or array and returns controls. By default, a physical binding that can
activate together with another action in the same map raises a conflict error. This includes aliases such as
mouse.left/mouse.button1 and modifier subsets such as key.r/ctrl+key.r. { allowConflict = true } permits it.
controls:rebind("rotate", "key.g")
controls:rebind("cancel", { "key.escape", "mouse.right" }, { allowConflict = true })Restores the declaration's default binding for this session and returns controls. rebind and resetBinding
remain session scoped; neither writes player preferences.
controls:setPreferredBindings(action, bindings, options) saves a player binding override and applies it
immediately. Use a string, an array of up to eight bindings, or an empty array to unbind. The optional
{ allowConflict = true } uses the same conflict rule as rebind. resetPreferredBindings(action) removes the
saved override and applies the declaration default.
controls:getMode(action) returns hold or toggle. setMode(action, mode) saves an allowed choice;
resetMode(action) returns to the declaration default. resetAllPreferences() removes saved binding and mode
overrides for the entire map. These methods update live action state; a mode change clears active toggle state.
Preferences are client-wide and survive script reload. A later script rebind can temporarily supersede a saved
binding; a new player edit supersedes that session override.
Saved controls live in the instance's betamoon/controls.json.
Open Options → Controls for Minecraft and mod hotkeys, then select the BetaMoon tab for named actions.
Search or use the mouse wheel to find an action. A focused search box shows _ after the entered text. Hold
Backspace to erase search text.
Each binding has one row: click its displayed input to change it, + to add another, - to remove it, or
R to restore that action's default bindings and mode. Actions with several bindings show their row number.
After choosing a binding or +, press a key, mouse button, or scroll the wheel;
held Ctrl, Shift, and Alt become part of the binding.
Escape or Cancel binding cancels capture, Delete/Backspace removes the selected binding during capture, and Bind Escape binds Escape explicitly. A conflict names the other action and asks you to choose Use anyway or Cancel. Hold or Toggle appears on a binding row only when both modes are allowed: Hold stays active while pressed, while a wheel binding acts as a short pulse. Toggle switches on or off with each press or matching wheel event. Click the button to switch modes. Reset all requires a second click. An action that allows only one mode has no mode button. Select the Minecraft tab to return to the standard key bindings.
Registers a controller with a namespaced key and a stages table. The four stages are movement, look,
worldActions, and inventory. Omitted stages use native. Each declared mode is native, modify,
manual, or disabled. The returned controller has key, on(stage, callback), activate(options),
isActive(), and close(). controllers:get(key) returns nil when absent; getRequired(key) raises an error.
controller:activate { player = ctx.player, priority = 100 } requires the current local player during focused
gameplay and returns an
activation. Higher priority wins separately for each stage; newest wins ties. An activation exposes
submitIntent(intent), face(point), snapshot(), isActive(), and close(). It closes on focus loss,
screen/world/player change, death, and script unload. Submissions are valid for the next input cycle only.
Omit player to select the current local player when activating outside a player-scoped callback.
local controller = betamoon.input.controllers:add {
key = "mymod:adventure",
stages = { movement = "manual", worldActions = "manual", look = "manual" },
}
-- Call this from a gameplay callback that provides the local player handle.
local function beginAdventure(ctx)
local active = controller:activate { player = ctx.player, priority = 100 }
active:submitIntent { movement = { forward = 1, strafe = 0, jump = false, sneak = false } }
return active
endA modify movement callback receives nativeIntent and may return { movement = ... }, pass, or deny.
Movement values are finite in -1..1. A look callback receives nativeFacing and may return
{ facing = { yaw = number, pitch = number } }; pitch is -90..90. World action callbacks receive
nativeAction (attack, use, or heldBreaking), button, and held, and return pass or deny.
Inventory callbacks receive open, drop, hotbar, or hotbarWheel. Every callback receives stage,
tick, and a callback-scoped player handle. A manual stage defaults to zero movement, frozen look, or denied
actions without an intent or explicit pass. Both discrete clicks and held breaking are covered. A callback error
releases that stage to a lower controller or native control.
submitIntent { action = { kind = "attack", result = "pass" } } permits only the named native action on the next
input cycle. Passing an attack never permits held breaking. face { x = ..., y = ..., z = ... } schedules player
facing toward a point and returns false for a point at the
player's eye. Camera rigs and route following are separate later capabilities.
See the named actions and controller example for a mode-aware walk action and a fixed Hold action shown together in Controls.
Input maps, contexts, and controllers are cleaned up automatically on script unload. Hotkey callbacks are detached while their Controls entries remain as described above. Focus, screen, world, and player replacement synthesize releases before physical state is cleared, preventing stuck held actions. A map supports at most 128 actions and eight effective bindings per action. The client supports up to 256 retained native hotkeys, 256 maps, 256 active contexts, 256 controller registrations, 256 controller activations, and 1024 semantic transitions in one cycle.
- Installation
- Install scripts
- Getting started
- VS Code and LuaLS
- Examples
- Reload and compatibility
- Troubleshooting
- Changelog
Scripts and shared values
- Script lifecycle
- Packaged Lua mods
- Modules
- References and stacks
- Queries
- Overrides
- Minecraft constants
- Chat and positions
- Structured data
- Script configuration
- Particles
Blocks and items
- Multiblocks
- Blocks
- Block state and placement
- Block shapes and rendering
- Block callbacks
- Block ticks and redstone
- Items
- Fluids
- Item callbacks
- Tools and armor
- Materials
- Textures
Entities
- Entities
- Entity access and movement
- Entity lifecycle and data
- Entity health and loot
- Entity inventories and relationships
- Projectiles and pickups
- Entity presentation and sounds
- Living entities and AI
- Entity parts and sensors
Assets and audio
- Assets
- Models and animations
- Built-in Minecraft models
- Model appearances
- Blockbench workflow
- Audio
- Texture-pack overrides
Events and world actions
World generation
World systems
Inventories and screens
Recipes