> ## Documentation Index
> Fetch the complete documentation index at: https://docs.selvut.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle & Events

> Events currently supported by scripts.

Register events with `Script.on(eventName, callback)`.

```lua theme={null}
Script.on("enable", function()
  Chat.log("enabled")
end)

Script.on("disable", function()
  Script.stopAllTasks()
end)
```

## Events

| Event                | Payload                                                                                |
| -------------------- | -------------------------------------------------------------------------------------- |
| `enable`             | none                                                                                   |
| `disable`            | none                                                                                   |
| `tick`               | `{ tickNanos }`                                                                        |
| `post_tick`          | `{ tickNanos }`                                                                        |
| `player_tick`        | `{}`                                                                                   |
| `render2d`           | `{ delta, width, height }`                                                             |
| `render2d_top`       | `{ delta, width, height }`                                                             |
| `render3d`           | `{ partialTicks }`                                                                     |
| `key`                | `{ key, action }`                                                                      |
| `mouse_click`        | `{ button, action, cancel() }`                                                         |
| `mouse_scroll`       | `{ vertical, horizontal, cancel() }`                                                   |
| `chat`               | `{ message, text }`                                                                    |
| `player_move`        | `{ type, movement, cancel() }`                                                         |
| `move_input`         | `{ forward, strafe, jump, sneak, sprint, zero(), cancel() }`                           |
| `motion_input`       | `{ originalForward, originalStrafe, forward, strafe, reset(), cancel() }`              |
| `player_move_packet` | `{ stage, cancel() }`                                                                  |
| `screen_change`      | `{ screenName, previousScreenName, screenClass, previousScreenClass, opened, closed }` |
| `container_click`    | `{ slot, button, clickType, cancel() }`                                                |
| `world_join`         | `{ createdAtMillis }`                                                                  |
| `world_leave`        | `{ createdAtMillis }`                                                                  |
| `block_update`       | `{ x, y, z, chunkX, chunkZ, id, state }`                                               |
| `chunk_load`         | `{ chunkX, chunkZ }`                                                                   |
| `chunk_unload`       | `{ chunkX, chunkZ }`                                                                   |
| `packet_receive`     | packet event object                                                                    |
| `packet_send`        | packet event object                                                                    |
| `action_bar`         | `{ text }`                                                                             |
| `title`              | `{ text }`                                                                             |
| `sound_play`         | `{ id, category, x, y, z, volume, pitch, cancel() }`                                   |

`render2d` draws behind any open screen (chat, inventory, etc.), same as other HUD content. `render2d_top` has the identical payload but draws after the screen finishes rendering, so it appears on top -- use it for overlays that need to stay visible while a screen is open, like a chat link preview.

`sound_play` fires for every sound the client plays -- both server-broadcast and locally-triggered (entity hurt/step/ambient) -- so it can fully replace matched sounds via `cancel()` plus `Sound.play(...)`. Selvut's own custom sounds are excluded from the event so a replacement can't trigger itself again.

`chat` fires for every line that actually renders in chat (visible chat), not only messages that came from the server (true chat). It's intentionally broader than what client modules react to internally, so a script can see chat content a module wouldn't.

Packet event objects include `direction`, `className`, `simpleName`, `type`, `raw`, and `cancel()`. System chat packets also include `text` and `overlay`.

Cancelable packet callbacks may run off the client thread. Keep them short and avoid client-state APIs from those callbacks.
