# Adding a Bedrock Edition Packet This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. --- ## Naming Conventions - **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). - **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). --- ## Step 1: Identifying the Packet ID In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. For example: - `Disconnect` packet ID: `5` - `RequestChunkRadius` packet ID: `69` --- ## Step 2: Implementing a Clientbound Packet Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. ### 1. Create the Packet File Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: ```rust use std::io::{Error, Write}; use pumpkin_macros::packet; use crate::{codec::var_int::VarInt, serial::PacketWrite}; #[packet(5)] pub struct CDisconnect { pub reason: VarInt, pub skip_message: bool, pub message: String, pub filtered_message: String, } impl CDisconnect { #[must_use] pub const fn new(reason: i32, message: String) -> Self { Self { reason: VarInt(reason), skip_message: message.is_empty(), message, filtered_message: String::new(), } } } impl PacketWrite for CDisconnect { fn write(&self, writer: &mut W) -> Result<(), Error> { self.reason.write(writer)?; self.skip_message.write(writer)?; if !self.skip_message { self.message.write(writer)?; self.filtered_message.write(writer)?; } Ok(()) } } ``` ### 2. Export the Module In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: ```rust pub mod disconnect; pub use disconnect::*; ``` --- ## Step 3: Implementing a Serverbound Packet Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. ### 1. Create the Packet File Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: ```rust use pumpkin_macros::packet; use crate::{codec::var_int::VarInt, serial::PacketRead}; #[derive(PacketRead, Debug)] #[packet(69)] pub struct SRequestChunkRadius { pub chunk_radius: VarInt, pub max_chunk_radius: u8, } ``` If a packet requires custom parsing logic, implement `PacketRead` manually: ```rust use std::io::{Error, Read}; use crate::serial::PacketRead; impl PacketRead for SMyCustomPacket { fn read(reader: &mut R) -> Result { let chunk_radius = VarInt::read(reader)?; let max_chunk_radius = u8::read(reader)?; Ok(Self { chunk_radius, max_chunk_radius }) } } ``` ### 2. Export the Module In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: ```rust pub mod request_chunk_radius; pub use request_chunk_radius::*; ``` --- ## Step 4: Dispatching Incoming Packets in `pumpkin` Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: ```rust // Inside BedrockClient::handle_game_packet match packet.id { SRequestChunkRadius::PACKET_ID => { let packet = SRequestChunkRadius::read(reader)?; self.handle_request_chunk_radius(player, &packet); } _ => {} } ``` --- ## Step 5: Implementing the Handler Function Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: ```rust use super::*; use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; impl BedrockClient { pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { let requested_radius = packet.chunk_radius.0; // Clamp view distance within server configuration limits let clamped = requested_radius.clamp(2, 16); // Notify client that view radius was accepted self.try_enqueue_client_packet(&CChunkRadiusUpdated { chunk_radius: VarInt(clamped), }); } } ``` Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: ```rust pub mod request_chunk_radius; ``` --- ## Step 6: Sending Outgoing Packets To send a Bedrock packet to a connected client: ```rust // Enqueues the packet into the client's outgoing batch queue self.try_enqueue_client_packet(&CChunkRadiusUpdated { chunk_radius: VarInt(view_distance), }); ``` Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket.