---
title: "Packets"
description: "Match, cancel and send packets by stable name, with the decoded fields of each."
---

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

# Packets

The `packets` global sends; the `packet_send` and `packet_receive` [events](/events#network)
observe. Fields mirror the wire format, so coordinates arrive as raw `x, y, z` scalars rather
than [vec3](/vec3).

```lua
module:event("packet_receive", function(e)
if e.name == "minecraft:sound" then e:cancel() end   -- mute server-played sounds
end)
```

Packets are matched by stable snake_case names, the same strings `e.name` reports and
`packets.send` accepts. The events fire for every packet: unmapped ones arrive under their
vanilla protocol id (a name with a colon, like `"minecraft:set_entity_motion"`) carrying no
decoded fields, still cancellable. The names below add decoded fields and `packets.send`
support.

| Function | Does |
|----------|------|
| `packets.list()` | Sorted names `packets.send` can build |
| `packets.send(name, fields)` | Builds and sends a C2S packet, `true` on success |

`packets.send` raises on an unknown name and returns `false` when a required game object is
missing. It goes through the normal client pipeline, so it fires `packet_send` as well: a
module that cancels its own scripted packets swallows them.

```lua
local pos = player:position()
packets.send("move_position", { x = pos.x, y = pos.y + 0.05, z = pos.z, on_ground = false })
packets.send("client_command", { mode = "start_sprinting" })
packets.send("player_action", { action = "swap_item_with_offhand", x = 0, y = 0, z = 0 })

local target = world:player("Notch")
if target then
packets.send("attack_entity", { entity_id = target:id() })
end
```

For attacking, breaking blocks and clicking slots, [`interaction`](/interaction) is the
better tool: it gets swing timers and sequence numbers right. Raw packets are for when you
want the wire format yourself.

## Sendable packets

Optional fields show their defaults. `hand` is `"main"` / `"off"` (default `"main"`), `side`
is `"up"` · `"down"` · `"north"` · `"south"` · `"east"` · `"west"` (default `"up"`), and
`sequence` defaults to `0`.

<details open>
<summary>All 22 sendable packets</summary>

| Name | Fields |
|------|--------|
| `move_full` | `x, y, z, yaw, pitch, on_ground = true, horizontal_collision = false` |
| `move_position` | `x, y, z, on_ground = true, horizontal_collision = false` |
| `move_look` | `yaw, pitch, on_ground = true, horizontal_collision = false` |
| `move_on_ground` | `on_ground = true, horizontal_collision = false` |
| `client_command` | `mode` — `"stop_sleeping"` · `"start_sprinting"` · `"stop_sprinting"` · `"start_riding_jump"` · `"stop_riding_jump"` · `"open_inventory"` · `"start_fall_flying"`; `jump_height = 0` |
| `client_status` | `mode` — `"perform_respawn"` · `"request_stats"` |
| `hand_swing` | `hand` |
| `player_action` | `action` — `"start_destroy_block"` · `"abort_destroy_block"` · `"stop_destroy_block"` · `"drop_all_items"` · `"drop_item"` · `"release_use_item"` · `"swap_item_with_offhand"` · `"stab"`; `x, y, z, side, sequence` |
| `attack_entity` | `entity_id, sneaking = false` |
| `interact_entity` | `entity_id, hand, sneaking = false` |
| `interact_entity_at` | `entity_id, x, y, z` (hit point), `hand, sneaking = false` |
| `interact_block` | `x, y, z, side, hit_x/hit_y/hit_z` (default block center), `inside = false, hand, sequence` |
| `interact_item` | `hand, yaw, pitch` (default current rotation), `sequence` |
| `select_slot` | `slot` — hotbar `0–8` |
| `close_screen` | `sync_id` |
| `teleport_confirm` | `id` |
| `keep_alive` | `id` |
| `command` | `command` — without the leading `/` |
| `player_input` | `forward, backward, left, right, jump, sneak, sprint` — all `false` |
| `sign_update` | `x, y, z, front = true, line1..line4 = ""` — the text a sign editor would submit |
| `pick_item_from_block` | `x, y, z, include_data = false` |
| `pick_item_from_entity` | `entity_id, include_data = false` |

</details>

## Decoded event fields

Both events always carry `name`. The packets below decode their payload too; everything else
fires with `name` alone.

**Outgoing.** Most sendable packets decode with the same fields they take, except that
`move_*` carry `x/y/z` only when the position changed and `yaw/pitch` only when the look
changed. Five carry `name` only: `interact_entity`, `attack_entity`, `interact_entity_at`,
`pick_item_from_block`, `pick_item_from_entity`.

**Incoming.**

<details open>
<summary>All 16 decoded incoming packets</summary>

| Name | Fields |
|------|--------|
| `keep_alive` | `id` |
| `disconnect` | `reason` |
| `player_position_look` | `teleport_id, x, y, z, vel_x, vel_y, vel_z, yaw, pitch` |
| `entity_velocity` | `entity_id, vel_x, vel_y, vel_z` |
| `entity_position` | `entity_id, x, y, z, yaw, pitch, on_ground` |
| `entity_position_sync` | `entity_id, x, y, z, yaw, pitch, on_ground` |
| `entity_damage` | `entity_id, source_cause_id, source_direct_id` |
| `damage_tilt` | `entity_id, yaw` |
| `health_update` | `health, food, saturation` |
| `explosion` | `x, y, z, radius, knockback_x/y/z?` — knockback only when you were pushed |
| `block_update` | `x, y, z, block` — block id |
| `select_slot` | `slot` |
| `open_screen` | `sync_id, kind` — menu type id like `"minecraft:generic_9x6"`; `title` |
| `container_content` | `sync_id, revision, size` — the server syncing every slot of a handler |
| `container_slot` | `sync_id, revision, slot, item, count` — a single slot update |
| `open_sign_editor` | `x, y, z, front` — cancel it and answer with `sign_update` to write a sign without the editor |

</details>

:::caution
Packet events run on network threads. Collect data there and act in `tick`; never draw or
call `projection` from them.
:::

Source: https://aesthetic-docs.pages.dev/packets/index.md
