Pumpkin/pumpkin/src/net/bedrock/mod.rs
2026-08-03 21:16:07 +02:00

1417 lines
52 KiB
Rust

pub mod play;
use crossbeam::atomic::AtomicCell;
use std::{
collections::{BTreeMap, HashMap, HashSet},
io::{Cursor, Error, Write},
net::{Ipv4Addr, SocketAddrV4},
sync::{
Arc,
atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering},
},
time::UNIX_EPOCH,
};
use tracing::{debug, error, warn};
use bytes::Bytes;
use pumpkin_config::networking::compression::CompressionInfo;
use pumpkin_protocol::{
BClientPacket, PacketDecodeError, RawPacket,
bedrock::{
MTU, RAKNET_ACK, RAKNET_GAME_PACKET, RAKNET_NACK, RAKNET_VALID, RakReliability,
SPLIT_FRAME_MAX_CONTENT, SubClient, UDP_HEADER_SIZE,
ack::Acknowledge,
client::{
disconnect_player::CDisconnectPlayer,
level_chunk::CLevelChunk,
play_status::CPlayStatus,
raknet::connection::{
CAlreadyConnected, CConnectionBanned, CConnectionRequestAccepted, CDisconnect,
CNoFreeIncomingConnections,
},
resource_packs_info::{CResourcePacksInfo, ResourcePackEntry},
},
frame_set::{Frame, FrameSet},
packet_decoder::UDPNetworkDecoder,
packet_encoder::UDPNetworkEncoder,
server::{
animate::SAnimate,
block_pick_request::SBlockPickRequest,
client_cache_status::SClientCacheStatus,
client_to_server_handshake::SClientToServerHandshake,
command_request::SCommandRequest,
container_close::SContainerClose,
emote::SEmote,
interaction::SInteraction,
inventory_transaction::SInventoryTransaction,
loading_screen::SLoadingScreen,
login::SLogin,
mob_equipment::SMobEquipment,
player_action::SPlayerAction,
player_auth_input::SPlayerAuthInput,
raknet::{
connection::{
SConnectedPing, SConnectionLost, SConnectionRequest, SDisconnect,
SNewIncomingConnection,
},
open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2},
unconnected_ping::{SUnconnectedPing, SUnconnectedPingOpenConnections},
},
request_ability::SRequestAbility,
request_chunk_radius::SRequestChunkRadius,
request_network_settings::SRequestNetworkSettings,
resource_pack_response::SResourcePackResponse,
set_local_player_as_initialized::SSetLocalPlayerAsInitialized,
set_player_inventory_options::SSetPlayerInventoryOptions,
text::SText,
},
},
codec::u24,
packet::Packet,
serial::{PacketRead, PacketReadSlice},
};
use std::net::SocketAddr;
use tokio::{
net::UdpSocket,
sync::mpsc::{Receiver, Sender},
sync::{Mutex, RwLock, oneshot},
task::JoinHandle,
};
use tokio_util::{sync::CancellationToken, task::TaskTracker};
pub mod connection;
pub mod login;
pub mod open_connection;
pub mod unconnected;
use crate::{
entity::player::Player,
net::{DisconnectReason, GameProfile, PacketHandlerResult, PlayerConfig},
plugin::api::events::world::chunk_send::ChunkSend,
server::Server,
};
use arc_swap::ArcSwap;
use pumpkin_protocol::bedrock::server::login::ClientData;
use pumpkin_util::version::BedrockMinecraftVersion;
use pumpkin_world::level::SyncChunk;
pub struct OutgoingPacket {
pub data: Bytes,
pub completion: Option<oneshot::Sender<()>>,
}
impl OutgoingPacket {
pub const fn normal(data: Bytes) -> Self {
Self {
data,
completion: None,
}
}
pub const fn priority(data: Bytes, completion: oneshot::Sender<()>) -> Self {
Self {
data,
completion: Some(completion),
}
}
}
pub struct BedrockClient {
socket: Arc<UdpSocket>,
/// The client's IP address.
pub address: SocketAddr,
pub player: Mutex<Option<Arc<Player>>>,
pub version: AtomicCell<BedrockMinecraftVersion>,
pub client_data: ArcSwap<Option<Arc<ClientData>>>,
/// All Bedrock clients
/// This list is used to remove the client if the connection gets closed
pub be_clients: Arc<Mutex<HashMap<SocketAddr, Arc<Self>>>>,
tasks: TaskTracker,
rt_handle: tokio::runtime::Handle,
outgoing_packet_queue_send: Sender<OutgoingPacket>,
/// A queue of serialized packets to send to the network
outgoing_packet_queue_recv: Mutex<Option<Receiver<OutgoingPacket>>>,
outgoing_packet_priority_send: Sender<OutgoingPacket>,
outgoing_packet_priority_recv: Mutex<Option<Receiver<OutgoingPacket>>>,
/// The packet encoder for outgoing packets.
network_writer: Arc<RwLock<UDPNetworkEncoder>>,
/// The packet decoder for incoming packets.
network_reader: Mutex<UDPNetworkDecoder>,
_use_frame_sets: AtomicBool,
output_sequence_number: AtomicU32,
output_reliable_number: AtomicU32,
output_split_number: AtomicU16,
output_sequenced_index: AtomicU32,
output_ordered_index: AtomicU32,
/// The next form ID to use for custom forms.
pub next_form_id: AtomicU32,
pub inventory_opened: AtomicBool,
/// An notifier that is triggered when this client is closed.
close_token: CancellationToken,
last_seen: Arc<AtomicCell<std::time::Instant>>,
/// Store Fragments until the packet is complete
compounds: Arc<Mutex<HashMap<u16, Vec<Option<Frame>>>>>,
//input_sequence_number: AtomicU32,
received_sequences: Mutex<HashSet<u32>>,
pending_acks: Mutex<Vec<u32>>,
#[allow(clippy::type_complexity)]
unacked_outgoing_frames: Mutex<HashMap<u32, (u8, Vec<u8>, std::time::Instant)>>,
expected_order_index: Mutex<HashMap<u8, u32>>,
highest_sequence_index: Mutex<HashMap<u8, u32>>,
ordered_queues: Mutex<HashMap<u8, BTreeMap<u32, Frame>>>,
incoming_game_packet_send: Sender<RawPacket>,
incoming_game_packet_recv: Mutex<Option<Receiver<RawPacket>>>,
pending_profile: Mutex<Option<(GameProfile, PlayerConfig)>>,
}
impl BedrockClient {
#[must_use]
pub fn new(
socket: Arc<UdpSocket>,
address: SocketAddr,
be_clients: Arc<Mutex<HashMap<SocketAddr, Arc<Self>>>>,
) -> Self {
let (send, recv) = tokio::sync::mpsc::channel(4096);
let (priority_send, priority_recv) = tokio::sync::mpsc::channel(4096);
let (incoming_send, incoming_recv) = tokio::sync::mpsc::channel(4096);
let rt_handle = tokio::runtime::Handle::current();
Self {
socket,
player: Mutex::new(None),
address,
version: AtomicCell::new(BedrockMinecraftVersion::Unknown),
client_data: ArcSwap::new(Arc::new(None)),
be_clients,
network_writer: Arc::new(RwLock::new(UDPNetworkEncoder::new())),
network_reader: Mutex::new(UDPNetworkDecoder::new()),
tasks: TaskTracker::new(),
rt_handle,
outgoing_packet_queue_send: send,
outgoing_packet_queue_recv: Mutex::new(Some(recv)),
outgoing_packet_priority_send: priority_send,
outgoing_packet_priority_recv: Mutex::new(Some(priority_recv)),
_use_frame_sets: AtomicBool::new(false),
output_sequence_number: AtomicU32::new(0),
output_reliable_number: AtomicU32::new(0),
output_split_number: AtomicU16::new(0),
output_sequenced_index: AtomicU32::new(0),
output_ordered_index: AtomicU32::new(0),
next_form_id: AtomicU32::new(0),
inventory_opened: AtomicBool::new(false),
compounds: Arc::new(Mutex::new(HashMap::new())),
close_token: CancellationToken::new(),
last_seen: Arc::new(AtomicCell::new(std::time::Instant::now())),
received_sequences: Mutex::new(HashSet::new()),
pending_acks: Mutex::new(Vec::new()),
unacked_outgoing_frames: Mutex::new(HashMap::new()),
expected_order_index: Mutex::new(HashMap::new()),
highest_sequence_index: Mutex::new(HashMap::new()),
ordered_queues: Mutex::new(HashMap::new()),
//input_sequence_number: AtomicU32::new(0),
incoming_game_packet_send: incoming_send,
incoming_game_packet_recv: Mutex::new(Some(incoming_recv)),
pending_profile: Mutex::new(None),
}
}
pub async fn get_packet(&self) -> Option<RawPacket> {
let mut guard = self.incoming_game_packet_recv.lock().await;
let recv = guard.as_mut()?;
tokio::select! {
() = self.await_close_interrupt() => None,
packet = recv.recv() => packet,
}
}
pub fn start_outgoing_packet_task(self: &Arc<Self>) {
let client = self.clone();
self.spawn_task(async move {
let mut packet_receiver = {
let mut guard = client.outgoing_packet_queue_recv.lock().await;
guard
.take()
.expect("Outgoing packet receiver was already taken")
};
let mut priority_packet_receiver = {
let mut guard = client.outgoing_packet_priority_recv.lock().await;
guard
.take()
.expect("Outgoing packet receiver was already taken")
};
let mut interval = tokio::time::interval(std::time::Duration::from_millis(100));
while !client.close_token.is_cancelled() {
let mut packet = tokio::select! {
biased;
() = client.close_token.cancelled() => break,
res = priority_packet_receiver.recv() => match res {
Some(p) => p,
None => break,
},
_ = interval.tick() => {
// Check for timeout (10 seconds)
if client.last_seen.load().elapsed() > std::time::Duration::from_secs(10) {
debug!("Bedrock client {} timed out", client.address);
client.close().await;
break;
}
// Flush ACKs
let mut pending = client.pending_acks.lock().await;
if !pending.is_empty() {
let ack = Acknowledge::new(pending.clone());
pending.clear();
let _ = client.send_acknowledgement(&ack, RAKNET_ACK).await;
}
// Check retransmission
let now = std::time::Instant::now();
let mut resend = Vec::new();
{
let mut unacked = client.unacked_outgoing_frames.lock().await;
for (seq, (id, data, timestamp)) in unacked.iter_mut() {
if now.duration_since(*timestamp) > std::time::Duration::from_secs(1) {
resend.push((*seq, *id, data.clone()));
// Update timestamp
*timestamp = now;
// Limit resends per tick to avoid starvation
if resend.len() >= 50 {
break;
}
}
}
}
if !resend.is_empty() {
let encoder = client.network_writer.read().await;
for (seq, id, data) in resend {
debug!("Resending reliable sequence {} (ID: {})", seq, id);
if let Err(err) = encoder.write_packet(&data, client.address, &client.socket).await {
warn!("Failed to resend packet for sequence {}: {}", seq, err);
}
}
}
continue;
}
res = packet_receiver.recv() => match res {
Some(p) => p,
None => break,
},
};
// Encrypt the packet payload if encryption is enabled.
if packet.data.len() > 1 && packet.data[0] == 0xfe {
let mut encoder = client.network_writer.write().await;
if let Some(encryptor) = encoder.encryptor_mut() {
let mut data_to_encrypt = packet.data[1..].to_vec();
encryptor.encrypt(&mut data_to_encrypt);
let mut encrypted_payload = Vec::with_capacity(1 + data_to_encrypt.len());
encrypted_payload.push(0xfe);
encrypted_payload.extend_from_slice(&data_to_encrypt);
packet.data = encrypted_payload.into();
}
}
client
.send_framed_packet_data(packet.data.to_vec(), RakReliability::ReliableOrdered)
.await;
if let Some(completion) = packet.completion {
let _ = completion.send(());
}
}
});
}
pub async fn process_packet(self: &Arc<Self>, server: &Arc<Server>, packet: Bytes) {
self.last_seen.store(std::time::Instant::now());
if let Err(error) = self.handle_packet_payload(server, packet).await {
error!(
"Failed to handle packet payload for {}: {}",
self.address, error
);
self.kick(DisconnectReason::BadPacket, error.to_string())
.await;
}
}
pub async fn set_compression(&self, compression: CompressionInfo) {
self.network_reader
.lock()
.await
.set_compression(compression.threshold as usize);
self.network_writer
.write()
.await
.set_compression((compression.threshold as usize, compression.level));
}
pub async fn kick(&self, reason: DisconnectReason, message: String) {
self.send_game_packet(&CDisconnectPlayer::new(reason as i32, message))
.await;
self.close().await;
}
pub async fn send_chunks(&self, chunks: &[SyncChunk]) {
let player = self.player.lock().await.clone();
let Some(player) = player.as_ref() else {
debug!(
"send_chunks: player not set yet, dropping {} chunks",
chunks.len()
);
return;
};
let Some(server) = player.world().server.upgrade() else {
return;
};
let mut valid_chunks = Vec::with_capacity(chunks.len());
for chunk in chunks {
let mut event = ChunkSend::new(player.world(), chunk.clone());
server.plugin_manager.fire(&server, &mut event).await;
if !event.cancelled {
valid_chunks.push(chunk.clone());
}
}
if valid_chunks.is_empty() {
return;
}
let mut serialize_tasks = Vec::with_capacity(valid_chunks.len());
for chunk in valid_chunks {
serialize_tasks.push(tokio::task::spawn_blocking(move || {
let mut packet_payload = Vec::new();
let packet = CLevelChunk {
dimension: 0,
cache_enabled: false,
chunk: &chunk,
};
packet
.write_packet(&mut packet_payload)
.map(|()| packet_payload)
}));
}
let mut encoded_payloads = Vec::with_capacity(serialize_tasks.len());
for task in serialize_tasks {
match task.await {
Ok(Ok(payload)) => encoded_payloads.push(payload),
Ok(Err(e)) => error!("Failed to serialize Bedrock chunk: {:?}", e),
Err(e) => error!("Join error in Bedrock chunk serialization: {:?}", e),
}
}
let mut packets_to_enqueue = Vec::with_capacity(encoded_payloads.len());
{
let encoder = self.network_writer.read().await;
for payload in encoded_payloads {
let mut packet_buf = Vec::new();
match encoder.write_game_packet(
CLevelChunk::PACKET_ID as u16,
SubClient::Main,
SubClient::Main,
&payload,
&mut packet_buf,
) {
Ok(()) => packets_to_enqueue.push(packet_buf),
Err(err) => error!("Failed to write game packet wrapper: {err}"),
}
}
}
for packet_buf in packets_to_enqueue {
self.enqueue_packet_data(packet_buf.into()).await;
}
}
pub async fn enqueue_packet<P: BClientPacket>(&self, packet: &P) {
let mut packet_buf = Vec::new();
match self.write_game_packet(packet, &mut packet_buf).await {
Ok(()) => {
let payload = Bytes::from(packet_buf);
let player = self.player.lock().await.clone();
let cancelled = if let Some(player) = player.as_ref() {
player
.fire_packet_sent_no_obj(P::PACKET_ID, payload.clone())
.await
} else {
false
};
if !cancelled {
self.enqueue_packet_data(payload).await;
}
}
Err(err) => error!("Failed to write game packet: {err}"),
}
}
pub async fn enqueue_packet_internal<P: BClientPacket>(&self, packet: &P) {
let mut packet_buf = Vec::new();
match self.write_game_packet(packet, &mut packet_buf).await {
Ok(()) => self.enqueue_packet_data(packet_buf.into()).await,
Err(err) => error!("Failed to write game packet: {err}"),
}
}
pub fn try_enqueue_packet<P: BClientPacket>(&self, packet: &P) {
let mut packet_buf = Vec::new();
let mut packet_payload = Vec::new();
if let Err(err) = packet.write_packet(&mut packet_payload) {
error!("Failed to write packet for try_enqueue_packet: {err}");
return;
}
{
let Ok(network_writer) = self.network_writer.try_read() else {
debug!("Failed to lock network writer for try_enqueue_packet");
return;
};
if let Err(err) = network_writer.write_game_packet(
P::PACKET_ID as u16,
SubClient::Main,
SubClient::Main,
&packet_payload,
&mut packet_buf,
) {
error!("Failed to write game packet for try_enqueue_packet: {err}");
return;
}
}
self.try_enqueue_packet_data(packet_buf.into());
}
/// Queues a clientbound packet to be sent to the connected client. Queued chunks are sent
/// in-order to the client
///
/// # Arguments
///
/// * `packet`: A reference to a packet object implementing the `ClientPacket` trait.
pub async fn enqueue_packet_data(&self, packet_data: Bytes) {
if let Err(err) = self
.outgoing_packet_queue_send
.send(OutgoingPacket::normal(packet_data))
.await
{
// This is expected to fail if we are closed
if !self.is_closed() {
error!("Failed to add packet to the outgoing packet queue for client: {err}");
}
}
}
pub fn try_enqueue_packet_data(&self, packet_data: Bytes) {
if let Err(err) = self
.outgoing_packet_queue_send
.try_send(OutgoingPacket::normal(packet_data))
{
match err {
tokio::sync::mpsc::error::TrySendError::Full(_) => {
debug!(
"Failed to add packet to the outgoing packet queue for client: channel full"
);
}
tokio::sync::mpsc::error::TrySendError::Closed(_) => {
if !self.is_closed() {
error!(
"Failed to add packet to the outgoing packet queue for client: channel closed"
);
}
}
}
}
}
pub fn write_raw_packet<P: BClientPacket>(
packet: &P,
mut write: impl Write,
) -> Result<(), Error> {
write.write_all(&[P::PACKET_ID as u8])?;
packet.write_packet(write)
}
pub async fn write_game_packet<P: BClientPacket>(
&self,
packet: &P,
write: impl Write,
) -> Result<(), Error> {
let mut packet_payload = Vec::new();
packet.write_packet(&mut packet_payload)?;
let encoder = self.network_writer.read().await;
encoder.write_game_packet(
P::PACKET_ID as u16,
SubClient::Main,
SubClient::Main,
&packet_payload,
write,
)
}
pub async fn send_offline_packet<P: BClientPacket>(
packet: &P,
addr: SocketAddr,
socket: &UdpSocket,
) {
let mut data = Vec::new();
if let Err(err) = Self::write_raw_packet(packet, &mut data) {
error!("Failed to write offline packet: {err}");
return;
}
// We dont care if it works, if not the client will try again!
let _ = socket.send_to(&data, addr).await;
}
pub async fn send_game_packet<P: BClientPacket>(&self, packet: &P) {
let mut packet_buf = Vec::new();
match self.write_game_packet(packet, &mut packet_buf).await {
Ok(()) => {
let payload = Bytes::from(packet_buf);
let player = self.player.lock().await.clone();
let cancelled = if let Some(player) = player.as_ref() {
player
.fire_packet_sent_no_obj(P::PACKET_ID, payload.clone())
.await
} else {
false
};
if cancelled {
return;
}
let (tx, rx) = oneshot::channel();
if let Err(err) = self
.outgoing_packet_priority_send
.send(OutgoingPacket::priority(payload, tx))
.await
{
if !self.is_closed() {
error!("Failed to add priority packet to the outgoing packet queue: {err}");
}
} else {
let _ = rx.await;
}
}
Err(err) => error!("Failed to write game packet: {err}"),
}
}
pub async fn write_game_packet_to_set<P: BClientPacket>(
&self,
packet: &P,
frame_set: &mut FrameSet,
) {
let mut payload = Vec::new();
match self.write_game_packet(packet, &mut payload).await {
Ok(()) => {
frame_set.frames.push(Frame::new_unreliable(payload));
}
Err(err) => error!("Failed to write game packet to set: {err}"),
}
}
pub async fn send_framed_packet<P: BClientPacket>(
&self,
packet: &P,
reliability: RakReliability,
) {
let mut packet_buf = Vec::new();
match Self::write_raw_packet(packet, &mut packet_buf) {
Ok(()) => self.send_framed_packet_data(packet_buf, reliability).await,
Err(err) => error!("Failed to write framed packet: {err}"),
}
}
pub async fn send_framed_packet_data(
&self,
packet_buf: Vec<u8>,
mut reliability: RakReliability,
) {
let mut split_size = 0;
let mut split_id = 0;
let mut order_index = 0;
let mut max_content_len =
MTU - UDP_HEADER_SIZE - 12 - if reliability.is_ordered() { 4 } else { 0 };
let count = if packet_buf.len() > max_content_len {
reliability = RakReliability::ReliableOrdered;
split_id = self.output_split_number.fetch_add(1, Ordering::Relaxed);
max_content_len = SPLIT_FRAME_MAX_CONTENT;
split_size = packet_buf.len().div_ceil(max_content_len) as u32;
split_size as usize
} else {
1
};
if reliability.is_ordered() {
order_index = self.output_ordered_index.fetch_add(1, Ordering::Relaxed);
}
for i in 0..count {
let end = if i + 1 == count && !packet_buf.len().is_multiple_of(max_content_len) {
packet_buf.len() % max_content_len
} else {
max_content_len
};
let chunk = &packet_buf[i * max_content_len..i * max_content_len + end];
let mut frame_set = FrameSet {
sequence: u24(0),
frames: Vec::with_capacity(1),
};
let mut frame = Frame {
payload: chunk.to_vec(),
reliability,
split_index: i as u32,
reliable_number: 0,
sequence_index: 0,
order_index,
order_channel: 0,
split_size,
split_id,
};
if reliability.is_reliable() {
frame.reliable_number = self.output_reliable_number.fetch_add(1, Ordering::Relaxed);
}
if reliability.is_sequenced() {
frame.sequence_index = self.output_sequenced_index.fetch_add(1, Ordering::Relaxed);
}
frame_set.frames.push(frame);
let id = if i == 0 { 0x84 } else { 0x8c };
self.send_frame_set(frame_set, id).await;
}
}
pub async fn send_frame_set(&self, mut frame_set: FrameSet, id: u8) {
let sequence = self.output_sequence_number.fetch_add(1, Ordering::Relaxed);
frame_set.sequence = u24(sequence);
let mut frame_set_buf = Vec::new();
if let Err(err) = frame_set.write_packet_data(&mut frame_set_buf, id) {
error!("Failed to write frame set data: {err}");
return;
}
if frame_set.frames.iter().any(|f| f.reliability.is_reliable()) {
self.unacked_outgoing_frames.lock().await.insert(
sequence,
(id, frame_set_buf.clone(), std::time::Instant::now()),
);
}
if let Err(err) = self
.network_writer
.read()
.await
.write_packet(&frame_set_buf, self.address, &self.socket)
.await
&& !self.is_closed()
{
warn!("Failed to send packet to client {}: {}", self.address, err);
self.close_token.cancel();
}
}
pub async fn close(&self) {
if self.is_closed() {
return;
}
self.close_token.cancel();
self.send_framed_packet(&CDisconnect, RakReliability::Unreliable)
.await;
self.be_clients.lock().await.remove(&self.address);
}
pub async fn await_tasks(&self) {
self.tasks.close();
self.tasks.wait().await;
}
pub fn is_closed(&self) -> bool {
self.close_token.is_cancelled()
}
pub fn enqueue_spawn_packet(self: &Arc<Self>, entity: Arc<dyn crate::entity::EntityBase>) {
let client = self.clone();
self.spawn_task(async move {
entity.send_bedrock_spawn_packet(&client).await;
});
}
pub async fn send_acknowledgement(&self, ack: &Acknowledge, id: u8) -> Result<(), Error> {
let mut packet_buf = Vec::new();
ack.write(&mut packet_buf, id)?;
if let Err(err) = self
.network_writer
.read()
.await
.write_packet(&packet_buf, self.address, &self.socket)
.await
{
warn!("Failed to send acknowledgement to {}: {err}", self.address);
self.close().await;
return Err(err);
}
Ok(())
}
pub async fn handle_packet_payload(
self: &Arc<Self>,
server: &Arc<Server>,
packet: Bytes,
) -> Result<(), Error> {
let reader = &mut Cursor::new(packet);
match u8::read(reader)? {
RAKNET_ACK => {
self.handle_ack(&Acknowledge::read(reader)?).await;
}
RAKNET_NACK => {
self.handle_nack(&Acknowledge::read(reader)?).await;
}
RAKNET_VALID..=0x8d => {
self.handle_frame_set(server, FrameSet::read(reader)?)
.await?;
}
id => {
warn!("Bedrock: Received unknown packet header {id}");
}
}
Ok(())
}
async fn handle_ack(&self, ack: &Acknowledge) {
let mut unacked = self.unacked_outgoing_frames.lock().await;
for seq in &ack.sequences {
unacked.remove(seq);
}
}
async fn handle_nack(&self, nack: &Acknowledge) {
debug!("Received NACK for sequences: {:?}", nack.sequences);
let mut resend_data = Vec::new();
{
let unacked = self.unacked_outgoing_frames.lock().await;
for seq in &nack.sequences {
if let Some((_id, data, _timestamp)) = unacked.get(seq) {
resend_data.push(data.clone());
}
}
}
for data in resend_data {
if let Err(err) = self
.network_writer
.read()
.await
.write_packet(&data, self.address, &self.socket)
.await
{
warn!("Failed to resend packet from NACK: {}", err);
}
}
}
async fn handle_frame_set(
self: &Arc<Self>,
server: &Arc<Server>,
frame_set: FrameSet,
) -> Result<(), Error> {
let sequence = frame_set.sequence.0;
{
let mut received = self.received_sequences.lock().await;
if received.contains(&sequence) {
debug!("Received duplicate RakNet sequence: {}", sequence);
return Ok(());
}
received.insert(sequence);
// Limit the size of received sequences to avoid memory leak
if received.len() > 4096 {
// This is a very simple way to clear it, ideally we'd use a sliding window
received.clear();
}
}
self.pending_acks.lock().await.push(sequence);
for frame in frame_set.frames {
self.handle_frame(server, frame).await?;
}
Ok(())
}
async fn handle_frame(
self: &Arc<Self>,
server: &Arc<Server>,
mut frame: Frame,
) -> Result<(), Error> {
if frame.split_size > 0 {
let fragment_index = frame.split_index as usize;
let compound_id = frame.split_id;
let mut compounds = self.compounds.lock().await;
let entry = compounds.entry(compound_id).or_insert_with(|| {
let mut vec = Vec::with_capacity(frame.split_size as usize);
vec.resize_with(frame.split_size as usize, || None);
vec
});
if fragment_index >= entry.len() {
return Err(Error::other(format!(
"Fragment index {fragment_index} out of bounds for size {}",
entry.len()
)));
}
entry[fragment_index] = Some(frame);
// Check if all fragments are received
if entry.iter().any(Option::is_none) {
return Ok(());
}
let mut frames_opt = compounds
.remove(&compound_id)
.ok_or_else(|| Error::other("Compound ID vanished"))?;
let total_len: usize = frames_opt.iter().flatten().map(|f| f.payload.len()).sum();
let mut merged = Vec::with_capacity(total_len);
for f in frames_opt.iter().flatten() {
merged.extend_from_slice(&f.payload);
}
frame = frames_opt[0]
.take()
.ok_or_else(|| Error::other("Failed to retrieve primary frame"))?;
frame.payload = merged;
frame.split_size = 0;
}
// Handling Sequencing
if frame.reliability.is_sequenced() {
let mut highest_sequenced = self.highest_sequence_index.lock().await;
let current_highest = highest_sequenced.entry(frame.order_channel).or_insert(0);
if frame.sequence_index < *current_highest {
return Ok(());
}
*current_highest = frame.sequence_index;
}
// Handling Ordering
if frame.reliability.is_ordered() {
let mut expected_order = self.expected_order_index.lock().await;
let expected = expected_order.entry(frame.order_channel).or_insert(0);
if frame.order_index == *expected {
*expected += 1;
self.process_frame_payload(server, frame.payload).await?;
// Check for queued frames
let mut ordered_queues = self.ordered_queues.lock().await;
if let Some(queue) = ordered_queues.get_mut(&frame.order_channel) {
while let Some(next_frame) = queue.remove(expected) {
*expected += 1;
self.process_frame_payload(server, next_frame.payload)
.await?;
}
}
} else if frame.order_index > *expected {
let mut ordered_queues = self.ordered_queues.lock().await;
let queue = ordered_queues
.entry(frame.order_channel)
.or_insert_with(BTreeMap::new);
queue.insert(frame.order_index, frame);
}
// If frame.order_index < *expected, it's an old frame, discard it.
} else {
self.process_frame_payload(server, frame.payload).await?;
}
Ok(())
}
async fn process_frame_payload(
self: &Arc<Self>,
server: &Arc<Server>,
payload: Vec<u8>,
) -> Result<(), Error> {
if payload.is_empty() {
return Ok(());
}
let id = payload[0];
if id == RAKNET_GAME_PACKET as u8 {
// Decompress the batch
let decompressed_payload = self
.get_packet_payload(payload)
.await
.ok_or_else(|| Error::other("Failed to decompress game packet batch"))?;
// Loop through the decompressed buffer to extract ALL batched packets
let mut cursor = Cursor::new(decompressed_payload);
while (cursor.position() as usize) < cursor.get_ref().len() {
let game_packet = self
.network_reader
.lock()
.await
.get_game_packet(&mut cursor)
.map_err(|e| Error::other(e.to_string()))?;
self.handle_game_packet(server, game_packet).await?;
}
} else {
// It's an internal RakNet message (like SConnectedPing)
let mut cursor = Cursor::new(payload);
let _id = u8::read(&mut cursor)?; // consume ID byte
self.handle_raknet_packet(i32::from(id), cursor).await?;
}
Ok(())
}
async fn handle_game_packet(
&self,
_server: &Arc<Server>,
packet: RawPacket,
) -> Result<(), Error> {
if let Err(err) = self.incoming_game_packet_send.send(packet).await {
debug!("Failed to send game packet to session task: {err}");
}
Ok(())
}
pub async fn handle_login_sequence(
self: &Arc<Self>,
server: &Arc<Server>,
) -> PacketHandlerResult {
while let Some(packet) = self.get_packet().await {
let payload = &mut Cursor::new(&packet.payload);
match packet.id {
SRequestNetworkSettings::PACKET_ID => {
let packet = match SRequestNetworkSettings::read(payload) {
Ok(p) => p,
Err(err) => {
error!("Failed to read SRequestNetworkSettings: {err}");
continue;
}
};
self.handle_request_network_settings(packet, server).await;
}
SLogin::PACKET_ID => {
let packet = match SLogin::read(payload) {
Ok(p) => p,
Err(err) => {
error!("Failed to read SLogin: {err}");
self.kick(DisconnectReason::BadPacket, err.to_string())
.await;
return PacketHandlerResult::Stop;
}
};
match self.handle_login(packet, server).await {
Ok(Some(result)) => return result,
Ok(None) => {} // encryption enabled, wait for handshake
Err(err) => {
self.kick(DisconnectReason::Unknown, err.to_string()).await;
return PacketHandlerResult::Stop;
}
}
}
SClientToServerHandshake::PACKET_ID => {
let _packet = match SClientToServerHandshake::read(payload) {
Ok(p) => p,
Err(err) => {
error!("Failed to read SClientToServerHandshake: {err}");
continue;
}
};
let pending = self.pending_profile.lock().await.take();
if let Some((profile, new_config)) = pending {
self.enqueue_packet_internal(&CPlayStatus::LoginSuccess)
.await;
let br_config = &server.advanced_config.resource_pack.bedrock;
let mut entries = Vec::new();
if br_config.enabled {
for pack in &br_config.packs {
entries.push(ResourcePackEntry {
uuid: pack.uuid,
version: pack.version.clone(),
size: pack.size,
download_url: pack.download_url.clone(),
content_key: pack.content_key.clone(),
sub_pack_name: pack.sub_pack_name.clone(),
content_id: pack.content_id.clone(),
has_scripts: pack.has_scripts,
addon_pack: pack.addon_pack,
rtx_enabled: pack.rtx_enabled,
});
}
}
let packs_info = CResourcePacksInfo {
resource_pack_required: br_config.force,
has_addon_packs: false,
has_scripts: false,
is_vibrant_visuals_force_disabled: false,
world_template_id: uuid::Uuid::nil(),
world_template_version: String::new(),
resource_packs: entries,
};
self.enqueue_packet_internal(&packs_info).await;
return PacketHandlerResult::ReadyToPlay(profile, new_config);
}
error!("Received ClientToServerHandshake but no pending profile was found.");
self.kick(DisconnectReason::BadPacket, "Handshake error".into())
.await;
return PacketHandlerResult::Stop;
}
_ => {
debug!(
"Received unexpected game packet {} during login sequence",
packet.id
);
}
}
}
PacketHandlerResult::Stop
}
pub async fn progress_player_packets(
self: &Arc<Self>,
player: &Arc<Player>,
server: &Arc<Server>,
) {
while let Some(packet) = self.get_packet().await {
let mut event = crate::plugin::server::packet::PacketReceivedEvent::new(
player.clone(),
packet.id,
packet.payload.clone(),
);
server.plugin_manager.fire(server, &mut event).await;
if event.cancelled {
continue;
}
if let Err(err) = self.handle_play_packet(player, server, packet).await {
error!("Failed to handle Bedrock play packet: {err}");
}
}
}
pub async fn handle_play_packet(
&self,
player: &Arc<Player>,
server: &Arc<Server>,
packet: RawPacket,
) -> Result<(), Error> {
let payload = &packet.payload[..];
let reader = &mut &payload[..];
match packet.id {
SClientCacheStatus::PACKET_ID => {
// TODO
}
SResourcePackResponse::PACKET_ID => {
self.handle_resource_pack_response(SResourcePackResponse::read(reader)?, server)
.await;
}
SPlayerAuthInput::PACKET_ID => {
self.handle_player_auth_input(player, SPlayerAuthInput::read(reader)?, server)
.await;
}
SRequestChunkRadius::PACKET_ID => {
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
.await;
}
SInventoryTransaction::PACKET_ID => {
self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await;
}
pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::PACKET_ID => {
self.handle_item_stack_request(player, pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::read(reader)?).await;
}
SInteraction::PACKET_ID => {
self.handle_interaction(player, SInteraction::read(reader)?)
.await;
}
SContainerClose::PACKET_ID => {
self.handle_container_close(player, SContainerClose::read(reader)?)
.await;
}
SText::PACKET_ID => {
self.handle_chat_message(server, player, SText::read_slice(reader)?)
.await;
}
SCommandRequest::PACKET_ID => {
self.handle_chat_command(player, server, SCommandRequest::read_slice(reader)?)
.await;
}
SSetLocalPlayerAsInitialized::PACKET_ID => {
self.handle_set_local_player_as_initialized(
player,
&SSetLocalPlayerAsInitialized::read(reader)?,
);
}
SSetPlayerInventoryOptions::PACKET_ID => {
let _ = SSetPlayerInventoryOptions::read(reader)?;
// Ignore for now
}
SPlayerAction::PACKET_ID => {
self.handle_player_action(player, server, SPlayerAction::read(reader)?)
.await;
}
SAnimate::PACKET_ID => {
self.handle_animate(player, server, &SAnimate::read(reader)?).await;
}
SEmote::PACKET_ID => {
self.handle_emote(player, server, SEmote::read_slice(reader)?).await;
}
// SEmoteList::PACKET_ID => {
// self.handle_emote_list(player, server, SEmoteList::read(reader)?);
// }
pumpkin_protocol::bedrock::server::modal_form_response::SModalFormResponse::PACKET_ID => {
self.handle_modal_form_response(
player,
server,
pumpkin_protocol::bedrock::server::modal_form_response::SModalFormResponse::read_slice(
reader,
)?,
)
.await;
}
SLoadingScreen::PACKET_ID => {
// Ignore for now
}
SBlockPickRequest::PACKET_ID => {
self.handle_block_pick_request(player, SBlockPickRequest::read(reader)?)
.await;
}
SRequestAbility::PACKET_ID => {
self.handle_request_ability(player, SRequestAbility::read(reader)?)
.await;
}
SMobEquipment::PACKET_ID => {
self.handle_mob_equipment(server, player, SMobEquipment::read(reader)?)
.await;
}
_ => {
warn!("Bedrock: Received Unknown Game packet: {}", packet.id);
}
}
Ok(())
}
async fn handle_raknet_packet(
self: &Arc<Self>,
packet_id: i32,
mut payload: Cursor<Vec<u8>>,
) -> Result<(), Error> {
let reader = &mut payload;
match packet_id {
SConnectionRequest::PACKET_ID => {
let request = SConnectionRequest::read(reader)?;
self.send_framed_packet(
&CConnectionRequestAccepted::new(
self.address,
0,
[SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 19132)); 10],
request.time,
UNIX_EPOCH.elapsed().unwrap().as_millis() as u64,
),
RakReliability::Unreliable,
)
.await;
}
SNewIncomingConnection::PACKET_ID => {
self.handle_new_incoming_connection(&SNewIncomingConnection::read(reader)?);
}
SConnectedPing::PACKET_ID => {
self.handle_connected_ping(SConnectedPing::read(reader)?)
.await;
}
SDisconnect::PACKET_ID | SConnectionLost::PACKET_ID => {
self.close().await;
}
_ => {
warn!("Bedrock: Received Unknown RakNet Online packet: {packet_id}");
}
}
Ok(())
}
#[expect(clippy::too_many_lines)]
pub async fn handle_offline_packet(
server: &Server,
packet_id: u8,
payload: &mut Cursor<&[u8]>,
addr: SocketAddr,
socket: &UdpSocket,
be_clients: &Arc<Mutex<HashMap<SocketAddr, Arc<Self>>>>,
) -> Result<(), Error> {
let packet_id_i32 = i32::from(packet_id);
if packet_id_i32 == SOpenConnectionRequest1::PACKET_ID {
let is_banned = {
let mut banned_ips = server.data.banned_ip_list.write().await;
banned_ips.get_entry(&addr.ip()).is_some()
};
if is_banned {
Self::send_offline_packet(
&CConnectionBanned::new(server.server_guid),
addr,
socket,
)
.await;
return Ok(());
}
let player_count = {
let status = server.get_status().lock().await;
status
.status_response
.players
.as_ref()
.map_or(0, |p| p.online) as u32
};
if player_count >= server.advanced_config.networking.bedrock.max_players {
Self::send_offline_packet(
&CNoFreeIncomingConnections::new(server.server_guid),
addr,
socket,
)
.await;
return Ok(());
}
let old_client = {
let mut clients_guard = be_clients.lock().await;
clients_guard.remove(&addr)
};
if let Some(client) = old_client {
debug!(
"Closing old Bedrock client connection for {} due to new connection request",
addr
);
client.close().await;
}
}
match packet_id_i32 {
SUnconnectedPing::PACKET_ID => {
Self::handle_unconnected_ping(
server,
SUnconnectedPing::read(payload)?,
addr,
socket,
)
.await;
}
SUnconnectedPingOpenConnections::PACKET_ID => {
let packet = SUnconnectedPingOpenConnections::read(payload)?;
Self::handle_unconnected_ping(
server,
SUnconnectedPing {
time: packet.time,
magic: packet.magic,
client_guid: packet.client_guid,
},
addr,
socket,
)
.await;
}
SOpenConnectionRequest1::PACKET_ID => {
Self::handle_open_connection_1(
server,
SOpenConnectionRequest1::read(payload)?,
addr,
socket,
)
.await;
}
SOpenConnectionRequest2::PACKET_ID => {
let is_already_connected = {
let clients_guard = be_clients.lock().await;
clients_guard.contains_key(&addr)
};
if is_already_connected {
Self::send_offline_packet(
&CAlreadyConnected::new(server.server_guid),
addr,
socket,
)
.await;
return Ok(());
}
Self::handle_open_connection_2(
server,
SOpenConnectionRequest2::read(payload)?,
addr,
socket,
)
.await;
}
_ => error!("Bedrock: Received Unknown RakNet Offline packet: {packet_id}"),
}
Ok(())
}
pub async fn await_close_interrupt(&self) {
self.close_token.cancelled().await;
}
pub async fn get_packet_payload(&self, packet: Vec<u8>) -> Option<Vec<u8>> {
let mut network_reader = self.network_reader.lock().await;
tokio::select! {
() = self.await_close_interrupt() => {
debug!("Canceling player packet processing");
None
},
packet_result = network_reader.get_packet_payload(packet) => {
match packet_result {
Ok(packet) => Some(packet),
Err(err) => {
if !matches!(err, PacketDecodeError::ConnectionClosed) {
warn!("Failed to decode packet from client: {err}");
let text = format!("Error while reading incoming packet {err}");
self.kick(DisconnectReason::BadPacket, text).await;
}
None
}
}
}
}
}
pub fn spawn_task<F>(&self, task: F) -> Option<JoinHandle<F::Output>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
if self.close_token.is_cancelled() {
None
} else {
let _guard = self.rt_handle.enter();
Some(self.tasks.spawn(task))
}
}
}