Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ public class SetHeldItemClientboundPacket : IMinecraftClientboundPacket<SetHeldI
## Registering Packets
Before receiving or sending packets, you need to register them specifying packet ids for each game protocol version.
Packet registrations are made for each game phase, so you need to register them in the correct phase. Common phases are `Handshake`, `Login`, `Configuration` and `Play`.

In this example we are using Void predefined packet id mappings in `PacketIdDefinitions`.
```csharp
[Subscribe]
public void OnPlayerJoinedServer(PlayerJoinedServerEvent @event)
{
@event.Player.RegisterPacket<SetHeldItemClientboundPacket>(PacketIdDefinitions.ClientboundSetHeldItem);
}
```

You can also define your own mappings for packets you'd like to work with:
```csharp
[Subscribe]
public void OnPlayerJoinedServer(PlayerJoinedServerEvent @event)
Expand Down Expand Up @@ -132,7 +143,7 @@ await player.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, c
### Sending Packets to the Server
You can send packets to the server with `ILink.ServerChannel` instance.
```csharp
await player.GetLink().ServerChannel.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
await player.Link.ServerChannel.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPlayer.Link is nullable (it returns ILink?), so dereferencing it directly in the example can produce a NullReferenceException and also shows a pattern that won’t compile cleanly with nullable reference types enabled. Consider using the non-null link instance from the relevant event (when available) or explicitly guarding (e.g., assign player.Link to a local variable and throw a clear exception if it’s null) before calling into ServerChannel.

Copilot uses AI. Check for mistakes.
```

### Sending Packets to the [**`ILink`**](/docs/developing-plugins/network/links)
Expand All @@ -143,12 +154,12 @@ You can send packets to the link with `ILink.SendPacketAsync` method.
- If the packet has both interfaces, it will be sent only to the client.
- If the packet has neither interface, `InvalidOperationException` will be thrown.
```csharp
await player.GetLink().SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
await player.Link.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
```

When you want to explicitly send a packet to the server or client, `SendPacketAsync` has an overload that specifies the destination side.
```csharp
await player.GetLink().SendPacketAsync(Side.Client, new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
await player.Link.SendPacketAsync(Side.Client, new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
Comment on lines +157 to +162
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These examples dereference player.Link directly, but Link is nullable (ILink?). To avoid teaching a null-dereference pattern (and to keep the snippet compatible with nullable reference types), guard player.Link (or use the link provided by an event) before calling SendPacketAsync.

Copilot uses AI. Check for mistakes.
```

## Complete Example
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ public void OnPhaseChanged(PhaseChangedEvent @event)
new(0x51, ProtocolVersion.MINECRAFT_1_20_3),
new(0x53, ProtocolVersion.MINECRAFT_1_20_5),
new(0x63, ProtocolVersion.MINECRAFT_1_21_2),
new(0x62, ProtocolVersion.MINECRAFT_1_21_5)
new(0x62, ProtocolVersion.MINECRAFT_1_21_5),
new(0x67, ProtocolVersion.MINECRAFT_1_21_9)
]);

@event.Player.RegisterTransformations<SetHeldItemClientboundPacket>(Transformations);
// Register transformations for the channel that changed phase
@event.Player.RegisterTransformations<SetHeldItemClientboundPacket>(@event.Channel, Transformations);
}
```

Expand Down
Loading