> For the complete documentation index, see [llms.txt](https://docs.nursultan.fun/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nursultan.fun/actions/packets.md).

# Packets

A packet is a message between the client and the server. Packets show you things nothing else does: the exact moment of a knockback, system messages, what other players did.

## Two threads

Packets do **not** arrive on the client thread. Reading the packet's fields there is fine and expected; touching the world, the player or the inventory is not. Hop over first:

```kotlin
on<PacketReceiveEvent> { e ->
    val packet = e.packet()
    if (packet !is S2CEntityVelocityPacket) return@on
    if (packet.entityId() != player.id()) return@on

    onClientThread {
        control.jump()
    }
}
```

Forget `onClientThread` and you get rare, strange bugs that are painful to catch.

## What arrives

```kotlin
on<PacketReceiveEvent> { e -> e.packet() }   // from the server, S2CPacket
on<PacketSendEvent> { e -> e.packet() }      // from the client, C2SPacket
```

Packets are Kotlin records, so a `when` is the nicest way to take them apart:

```kotlin
on<PacketReceiveEvent> { e ->
    val text = when (val packet = e.packet()) {
        is S2CGameMessagePacket -> if (packet.overlay()) null else packet.content()
        is S2CProfilelessChatMessagePacket -> packet.message()
        is S2CChatMessagePacket -> packet.unsignedContent()
        else -> null
    }
    if (text != null && text.contains("duel")) {
        onClientThread { chat.sendToServer("/next") }
    }
}
```

## Cancelling

Both incoming and outgoing packets can be cancelled:

```kotlin
on<PacketSendEvent> { e ->
    if (e.packet() is C2SClientTickEndPacket) {
        e.cancel()
    }
}
```

Cancel carefully: the server expects certain behaviour from a client, and getting too creative ends in a kick.

## Sending your own

```kotlin
game.packets().send(C2SChatMessagePacket("hi", 0L, 0L))
game.packets().sendSequenced(C2SPlayerActionPacket(...))
```

`send` returns `false` when it could not go out — no connection, for instance.

`sendSequenced` is for packets the server numbers: block and item interactions. When in doubt, plain `send`.

## How packets are named

Names follow the vanilla ones: the `C2S` prefix is client to server, `S2C` is server to client.

The ones you will want most:

| Packet                                                                            | What it is about         |
| --------------------------------------------------------------------------------- | ------------------------ |
| `S2CEntityVelocityPacket`                                                         | someone got knocked back |
| `S2CGameMessagePacket`, `S2CChatMessagePacket`, `S2CProfilelessChatMessagePacket` | chat messages            |
| `S2CEntityDamagePacket`                                                           | someone took damage      |
| `S2CEntityStatusEffectPacket`                                                     | someone got an effect    |
| `S2CInventoryPacket`, `S2CScreenHandlerSlotUpdatePacket`                          | the inventory changed    |
| `S2CExplosionPacket`                                                              | an explosion             |
| `S2CEntityPositionPacket`, `S2CEntityMoveRelativePacket`                          | an entity moved          |
| `C2SMoveFullPacket`, `C2SMoveLookPacket`, `C2SMoveOnGroundPacket`                 | our movement             |
| `C2SChatMessagePacket`, `C2SCommandExecutionPacket`                               | what we type             |
| `C2SPlayerInteractEntityPacket`, `C2SPlayerActionPacket`                          | our actions              |

The full list is in your IDE's autocompletion: type `S2C` or `C2S` and look at what comes up. They are all pre-imported, so you never write an `import`.

## Tied to the Minecraft version

Packets are the only part of the API tied to the game version. When Minecraft updates, packet names and fields can change. If your script uses them, ship it alongside the matching client version and replace the packets jar in `.sdk/` when you update.

Scripts that never touch packets survive a Minecraft update untouched.
