
# `prediction`

Your own movement is simulated from real input; other players' input is guessed from their
observed motion.

| Function | Returns |
|----------|---------|
| `prediction.self(ticks)` | Your snapshot `ticks` ahead, `0` being now |
| `prediction.self_path(from, to)` | Your snapshots across `[from, to]` |
| `prediction.self_with_input(input, ticks)` | A one-off simulation holding a custom input |
| `prediction.player(entity_id, ticks)` | Another player's snapshot |
| `prediction.player_path(entity_id, from, to)` | Another player's snapshots across a range |
| `prediction.velocity(entity_id)` | De-smeared per-tick velocity as [vec3](vec3.md) |

A snapshot is
`{pos, velocity, min, max, on_ground, fall_distance, horizontal_collision}`, where `pos`,
`velocity` and the `min`/`max` bounding-box corners are [vec3](vec3.md).

`input` is `{forward, backward, left, right, jump, sneak, sprint}`; omitted keys are `false`.

Simulations are cached per client tick and stepped lazily, so asking for tick 5 and then tick
8 only simulates three more. Tick 1200 is the exclusive cap, and anything past 1199 returns
`nil`.

Remote players arrive interpolated, which makes their raw per-tick delta unreliable.
`prediction.velocity` recovers the true motion and is what the simulation seeds from.

```lua
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, s.max, 0x80FF4040, 2)
            end
        end
    end
end)
```
