Services

Bridges to the client's built-in services: smooth rotations, freelook, inventory, the vanilla interaction manager and movement prediction.

rotation

Server-side rotations with smoothing, priorities and automatic freelook (your camera stays put while the "server-side" facing rotates).

FunctionWhat it does
rotation.set(yaw, pitch[, priority])asks for a rotation; returns true if it was accepted
rotation.look_at(x, y, z[, priority])rotates to face a point (measured from the player's eyes)
rotation.camera()rotates towards where the camera looks (priority 100)
rotation.active()returns true while a managed rotation is running
rotation.lock_item_use()blocks new rotations until the end of the tick

A request lasts one tick — to keep facing a direction, request it every tick (usually from pre_interaction or tick). A request with a lower priority than the active one is rejected.

module:event("pre_interaction", function()
    local target = world.player("Notch")
    if target then
        rotation.look_at(target.x, target.y + target.height / 2, target.z, 10)
    end
end)

freelook

FunctionWhat it does
freelook.active()returns true while freelook is on
freelook.activate() / deactivate()turns freelook on / off
freelook.lock() / unlock()freezes the camera (the mouse stops rotating it)
freelook.locked()returns true while the camera is frozen
freelook.yaw() / pitch()current camera angles

inventory

Actions are scheduled to a safe point of the tick and play nicely with movement and packets.

FunctionWhat it does
inventory.swap(a, b)swaps the items in two slots
inventory.drop(slot[, all])drops from a slot (all defaults to true — the whole stack)
inventory.merge(from, to)moves a stack onto another one
inventory.use(slot[, yaw, pitch])uses the item in a slot, optionally facing a direction first
inventory.item(slot)returns the item table {id, count, name}, or nil
inventory.find(item_id)returns the first slot holding that item, or nil
inventory.count(item_id)returns how many of that item you carry in total
inventory.slot()returns the selected hotbar slot (0–8)
inventory.set_slot(i)selects a hotbar slot (0–8)

Slot numbering

RangeMeaning
0crafting result
1–4crafting grid
5–8armor (helmet → boots)
9–35main inventory
36–44hotbar
45offhand
module:event("tick", function()
    local totem = inventory.find("minecraft:totem_of_undying")
    if totem and inventory.item(45).id ~= "minecraft:totem_of_undying" then
        inventory.swap(totem, 45)
    end
end)

interaction

The vanilla interaction manager: attacks, block/entity/item interactions, block breaking and screen-handler clicks. Everything goes through the vanilla client paths — correct packets, swing timers and sequence numbers. For raw packet control see Packets.

Hands are "main" / "off" (default "main"); sides are "up" | "down" | "north" | "south" | "east" | "west" (default "up"). Interactions return "success" | "fail" | "pass".

FunctionWhat it does
interaction.gamemode()current game mode, e.g. "survival"
interaction.attack(entity_id)attacks an entity (with a main-hand swing)
interaction.interact(entity_id[, hand])right-clicks an entity
interaction.use_item([hand])uses the held item
interaction.attack_with_piercing_weapon([hand])spear stab/lunge attack; false when not holding one
interaction.use_block(x, y, z[, side[, hand]])right-clicks a block (aims at its center)
interaction.attack_block(x, y, z[, side])starts breaking a block (the first hit)
interaction.update_breaking(x, y, z[, side])continues breaking; call every tick while "holding the button"
interaction.cancel_breaking()aborts the current breaking
interaction.break_block(x, y, z)finishes breaking instantly (creative / fully cracked)
interaction.breaking()true while a block is being broken
interaction.breaking_progress()breaking stage 0–10, -1 when idle
interaction.stop_using()releases bow / stops eating / lowers shield
interaction.swing([hand])swings an arm (animation + packet)
interaction.sync_id()sync id of the open screen handler (0 = inventory)
interaction.click_slot(sync_id, slot, button[, action])clicks a slot like a mouse click
interaction.click_button(sync_id, button)clicks a screen-handler button (stonecutter, loom, ...)
interaction.pick_block(x, y, z[, include_data])pick-block
interaction.pick_entity(entity_id[, include_data])pick-block on an entity
interaction.close_screen()closes the open screen (and tells the server)

click_slot actions: "pickup" (default), "quick_move" (shift-click), "swap" (button = hotbar slot 0–8, 40 = offhand), "clone", "throw", "quick_craft", "pickup_all".

module:event("pre_interaction", function()
    for _, p in ipairs(world.players() or {}) do
        if not p.is_self and p.distance < 3 then
            interaction.attack(p.id)
            break
        end
    end
end)

prediction

Vanilla-accurate movement physics simulation: "where will this player be in N ticks?". The local player is simulated from the real input; other players from input guessed from their observed motion. Simulations are cached per client tick and stepped lazily — asking for tick 5 and then tick 8 only simulates three more ticks. Max 1200 ticks.

FunctionWhat it does
prediction.self(ticks)your snapshot ticks ticks ahead (0 = now)
prediction.self_path(from, to)array of your snapshots for [from, to]
prediction.self_with_input(input, ticks)one-off simulation with a custom held input
prediction.player(entity_id, ticks)another player's snapshot
prediction.player_path(entity_id, from, to)array of another player's snapshots
prediction.velocity(entity_id)de-smeared per-tick velocity → vx, vy, vz

A snapshot is a table: {x, y, z, vel_x, vel_y, vel_z, min_x, min_y, min_z, max_x, max_y, max_z, on_ground, fall_distance, horizontal_collision} (min_*/max_* are the bounding box). input for self_with_input is a table of booleans: {forward, backward, left, right, jump, sneak, sprint} — omitted keys are false.

Remote players' network positions are interpolated, so their raw per-tick delta is unreliable — prediction.velocity returns the recovered true motion and is what the simulation itself seeds from.

module:event("render_3d", function(render)
    for _, p in ipairs(world.players() or {}) do
        if not p.is_self and p.distance < 20 then
            local s = prediction.player(p.id, 10)
            if s then
                render.box(s.min_x, s.min_y, s.min_z, s.max_x, s.max_y, s.max_z, 0x80FF4040, 2)
            end
        end
    end
end)