From 637aada913751e886ef394f30806b5cf3687b96b Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Mon, 30 Jun 2025 23:15:09 +0200 Subject: [PATCH] More Bedrock work --- pumpkin-protocol/src/bedrock/ack.rs | 62 ++++++ .../src/bedrock/client/connection.rs | 1 - pumpkin-protocol/src/bedrock/client/mod.rs | 4 +- .../src/bedrock/client/raknet/connection.rs | 34 ++++ .../src/bedrock/client/raknet/mod.rs | 3 + .../client/{ => raknet}/open_connection.rs | 2 + .../client/{ => raknet}/unconnected_pong.rs | 0 pumpkin-protocol/src/bedrock/frame_set.rs | 131 ++++++++++++ pumpkin-protocol/src/bedrock/mod.rs | 84 ++++++++ .../src/bedrock/packet_decoder.rs | 20 +- .../src/bedrock/server/connection.rs | 10 - pumpkin-protocol/src/bedrock/server/mod.rs | 4 +- .../src/bedrock/server/raknet/connection.rs | 25 +++ .../src/bedrock/server/raknet/mod.rs | 3 + .../server/{ => raknet}/open_connection.rs | 0 .../server/{ => raknet}/unconnected_ping.rs | 0 pumpkin-protocol/src/codec/mod.rs | 1 + pumpkin-protocol/src/codec/u24.rs | 64 ++++++ pumpkin-protocol/src/ser/mod.rs | 22 +- pumpkin/src/net/bedrock/connection.rs | 34 ++++ pumpkin/src/net/bedrock/mod.rs | 189 ++++++++++++++++-- pumpkin/src/net/bedrock/open_connection.rs | 4 +- pumpkin/src/net/bedrock/unconnected.rs | 8 +- 23 files changed, 646 insertions(+), 59 deletions(-) create mode 100644 pumpkin-protocol/src/bedrock/ack.rs delete mode 100644 pumpkin-protocol/src/bedrock/client/connection.rs create mode 100644 pumpkin-protocol/src/bedrock/client/raknet/connection.rs create mode 100644 pumpkin-protocol/src/bedrock/client/raknet/mod.rs rename pumpkin-protocol/src/bedrock/client/{ => raknet}/open_connection.rs (94%) rename pumpkin-protocol/src/bedrock/client/{ => raknet}/unconnected_pong.rs (100%) create mode 100644 pumpkin-protocol/src/bedrock/frame_set.rs delete mode 100644 pumpkin-protocol/src/bedrock/server/connection.rs create mode 100644 pumpkin-protocol/src/bedrock/server/raknet/connection.rs create mode 100644 pumpkin-protocol/src/bedrock/server/raknet/mod.rs rename pumpkin-protocol/src/bedrock/server/{ => raknet}/open_connection.rs (100%) rename pumpkin-protocol/src/bedrock/server/{ => raknet}/unconnected_ping.rs (100%) create mode 100644 pumpkin-protocol/src/codec/u24.rs create mode 100644 pumpkin/src/net/bedrock/connection.rs diff --git a/pumpkin-protocol/src/bedrock/ack.rs b/pumpkin-protocol/src/bedrock/ack.rs new file mode 100644 index 000000000..2d45971d4 --- /dev/null +++ b/pumpkin-protocol/src/bedrock/ack.rs @@ -0,0 +1,62 @@ +use pumpkin_macros::packet; + +use crate::{ClientPacket, codec::u24::U24, ser::NetworkWriteExt}; + +#[packet(0xC0)] +pub struct Ack { + sequences: Vec, +} + +impl Ack { + pub fn new(sequences: Vec) -> Self { + Self { sequences } + } +} + +impl Ack { + fn write_range( + start: u32, + end: u32, + mut write: impl std::io::Write, + ) -> Result<(), crate::ser::WritingError> { + if start == end { + write.write_u8_be(1)?; + U24::encode(&U24(start), &mut write)?; + } else { + write.write_u8_be(0)?; + U24::encode(&U24(start), &mut write)?; + U24::encode(&U24(end), &mut write)?; + } + Ok(()) + } +} + +impl ClientPacket for Ack { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + ) -> Result<(), crate::ser::WritingError> { + let mut buffer = Vec::new(); + let mut count = 0; + + let mut start = self.sequences[0]; + let mut end = start; + for seq in self.sequences.clone() { + if seq == end + 1 { + end = seq + } else { + Self::write_range(start, end, &mut buffer)?; + count += 1; + start = seq; + end = seq; + } + } + Self::write_range(start, end, &mut buffer)?; + count += 1; + + write.write_u16_be(count)?; + write.write_slice(&buffer)?; + + Ok(()) + } +} diff --git a/pumpkin-protocol/src/bedrock/client/connection.rs b/pumpkin-protocol/src/bedrock/client/connection.rs deleted file mode 100644 index 8b1378917..000000000 --- a/pumpkin-protocol/src/bedrock/client/connection.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pumpkin-protocol/src/bedrock/client/mod.rs b/pumpkin-protocol/src/bedrock/client/mod.rs index 6db4677e3..8c162ec4e 100644 --- a/pumpkin-protocol/src/bedrock/client/mod.rs +++ b/pumpkin-protocol/src/bedrock/client/mod.rs @@ -1,3 +1 @@ -pub mod connection; -pub mod open_connection; -pub mod unconnected_pong; +pub mod raknet; diff --git a/pumpkin-protocol/src/bedrock/client/raknet/connection.rs b/pumpkin-protocol/src/bedrock/client/raknet/connection.rs new file mode 100644 index 000000000..9f2fd92aa --- /dev/null +++ b/pumpkin-protocol/src/bedrock/client/raknet/connection.rs @@ -0,0 +1,34 @@ +use crate::ser::network_serialize_no_prefix; +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::codec::socket_address::SocketAddress; + +#[derive(Serialize)] +#[packet(0x10)] +pub struct CConnectionRequestAccepted { + client_address: SocketAddress, + system_index: u16, + #[serde(serialize_with = "network_serialize_no_prefix")] + system_addresses: Vec, + requested_timestamp: u64, + timestamp: u64, +} + +impl CConnectionRequestAccepted { + pub fn new( + client_address: SocketAddress, + system_index: u16, + system_addresses: Vec, + requested_timestamp: u64, + timestamp: u64, + ) -> Self { + Self { + client_address, + system_index, + system_addresses, + requested_timestamp, + timestamp, + } + } +} diff --git a/pumpkin-protocol/src/bedrock/client/raknet/mod.rs b/pumpkin-protocol/src/bedrock/client/raknet/mod.rs new file mode 100644 index 000000000..6db4677e3 --- /dev/null +++ b/pumpkin-protocol/src/bedrock/client/raknet/mod.rs @@ -0,0 +1,3 @@ +pub mod connection; +pub mod open_connection; +pub mod unconnected_pong; diff --git a/pumpkin-protocol/src/bedrock/client/open_connection.rs b/pumpkin-protocol/src/bedrock/client/raknet/open_connection.rs similarity index 94% rename from pumpkin-protocol/src/bedrock/client/open_connection.rs rename to pumpkin-protocol/src/bedrock/client/raknet/open_connection.rs index 0ca783735..e2fd1c247 100644 --- a/pumpkin-protocol/src/bedrock/client/open_connection.rs +++ b/pumpkin-protocol/src/bedrock/client/raknet/open_connection.rs @@ -28,6 +28,7 @@ impl COpenConnectionReply1 { #[derive(Serialize)] #[packet(0x08)] pub struct COpenConnectionReply2 { + magic: [u8; 16], server_guid: u64, client_address: SocketAddress, mtu: u16, @@ -37,6 +38,7 @@ pub struct COpenConnectionReply2 { impl COpenConnectionReply2 { pub fn new(server_guid: u64, client_address: SocketAddress, mtu: u16, security: bool) -> Self { Self { + magic: RAKNET_MAGIC, server_guid, client_address, mtu, diff --git a/pumpkin-protocol/src/bedrock/client/unconnected_pong.rs b/pumpkin-protocol/src/bedrock/client/raknet/unconnected_pong.rs similarity index 100% rename from pumpkin-protocol/src/bedrock/client/unconnected_pong.rs rename to pumpkin-protocol/src/bedrock/client/raknet/unconnected_pong.rs diff --git a/pumpkin-protocol/src/bedrock/frame_set.rs b/pumpkin-protocol/src/bedrock/frame_set.rs new file mode 100644 index 000000000..328483a65 --- /dev/null +++ b/pumpkin-protocol/src/bedrock/frame_set.rs @@ -0,0 +1,131 @@ +use std::io::Write; + +use bytes::Bytes; +use pumpkin_macros::packet; + +use crate::bedrock::{RAKNET_SPLIT, RakReliability}; +use crate::codec::u24::U24; +use crate::ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError}; +use crate::{ClientPacket, ServerPacket}; + +#[packet[0x80]] +pub struct FrameSet { + pub sequence: U24, + pub frames: Vec, +} + +impl ServerPacket for FrameSet { + fn read(mut read: impl std::io::Read) -> Result { + Ok(Self { + sequence: read.get_u24()?, + frames: Frame::read(read)?, + }) + } +} + +impl ClientPacket for FrameSet { + fn write_packet_data(&self, mut write: impl Write) -> Result<(), WritingError> { + write.write_u24_be(self.sequence)?; + for frame in &self.frames { + frame.write(&mut write)?; + } + Ok(()) + } +} + +#[derive(Default)] +pub struct Frame { + pub reliability: RakReliability, + pub payload: Bytes, + pub reliable_index: u32, + pub sequence_index: u32, + pub order_index: u32, + pub order_channel: u8, + pub split_size: u32, + pub split_id: u16, + pub split_index: u32, +} + +impl Frame { + pub fn read(mut read: impl std::io::Read) -> Result, crate::ser::ReadingError> { + let mut frames = Vec::new(); + + while let Ok(header) = read.get_u8_be() { + let reliability_id = (header & 0xE0) >> 5; + let reliability = match RakReliability::from_id(reliability_id) { + Some(reliability) => reliability, + None => { + return Err(ReadingError::Message(format!( + "Invalid RakReliability {reliability_id}" + ))); + } + }; + let split = (header & RAKNET_SPLIT) != 0; + let length = (read.get_u16_be()? as f32 / 8.0).ceil(); + + let reliable_index = if reliability.is_reliable() { + read.get_u24()?.0 + } else { + 0 + }; + + let sequence_index = if reliability.is_sequenced() { + read.get_u24()?.0 + } else { + 0 + }; + + let (order_index, order_channel) = if reliability.is_ordered() { + (read.get_u24()?.0, read.get_u8_be()?) + } else { + (0, 0) + }; + let (split_size, split_id, split_index) = if split { + (read.get_u32_be()?, read.get_u16_be()?, read.get_u32_be()?) + } else { + (0, 0, 0) + }; + let payload = read.read_boxed_slice(length as usize)?; + frames.push(Self { + reliability, + payload: payload.into(), + reliable_index, + sequence_index, + order_index, + order_channel, + split_size, + split_id, + split_index, + }); + } + + Ok(frames) + } + + fn write(&self, mut write: impl Write) -> Result<(), WritingError> { + let is_split = self.split_size > 0; + write.write_u8_be( + (self.reliability.to_id() >> 5) & if is_split { RAKNET_SPLIT } else { 0 }, + )?; + write.write_u16_be((self.payload.len() >> 3) as u16)?; + if self.reliability.is_reliable() { + write.write_u24_be(U24(self.reliable_index))?; + } + if self.reliability.is_sequenced() { + write.write_u24_be(U24(self.sequence_index))?; + } + if self.reliability.is_ordered() { + write.write_u24_be(U24(self.order_index))?; + write.write_u8_be(self.order_channel)?; + } + if is_split { + write.write_u32_be(self.split_size)?; + write.write_u16_be(self.split_id)?; + write.write_u32_be(self.split_index)?; + } + + write.write_slice(&self.payload).unwrap(); + + Ok(()) + } +} diff --git a/pumpkin-protocol/src/bedrock/mod.rs b/pumpkin-protocol/src/bedrock/mod.rs index 44ecc71f9..734e9955d 100644 --- a/pumpkin-protocol/src/bedrock/mod.rs +++ b/pumpkin-protocol/src/bedrock/mod.rs @@ -1,4 +1,6 @@ +pub mod ack; pub mod client; +pub mod frame_set; pub mod packet_decoder; pub mod packet_encoder; pub mod server; @@ -6,3 +8,85 @@ pub mod server; pub const RAKNET_MAGIC: [u8; 16] = [ 0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, ]; + +pub const RAKNET_VALID: u8 = 0x80; +pub const RAKNET_ACK: u8 = 0xC0; +pub const RAKNET_NACK: u8 = 0xA0; +pub const RAKNET_SPLIT: u8 = 0x10; + +#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)] +pub enum RakReliability { + Unreliable, + UnreliableSequenced, + Reliable, + #[default] + ReliableOrdered, + ReliableSequenced, + UnreliableWithAckReceipt, + ReliableWithAckReceipt, + ReliableOrderedWithAckReceipt, +} + +impl RakReliability { + pub fn is_reliable(&self) -> bool { + matches!( + self, + RakReliability::Reliable + | RakReliability::ReliableOrdered + | RakReliability::ReliableSequenced + | RakReliability::ReliableWithAckReceipt + | RakReliability::ReliableOrderedWithAckReceipt + ) + } + + pub fn is_sequenced(&self) -> bool { + matches!( + self, + RakReliability::ReliableSequenced | RakReliability::UnreliableSequenced + ) + } + + pub fn is_ordered(&self) -> bool { + matches!( + self, + RakReliability::UnreliableSequenced + | RakReliability::ReliableOrdered + | RakReliability::ReliableSequenced + | RakReliability::ReliableOrderedWithAckReceipt + ) + } + + pub fn is_order_exclusive(&self) -> bool { + matches!( + self, + RakReliability::ReliableOrdered | RakReliability::ReliableOrderedWithAckReceipt + ) + } + + pub fn from_id(id: u8) -> Option { + match id { + 0 => Some(RakReliability::Unreliable), + 1 => Some(RakReliability::UnreliableSequenced), + 2 => Some(RakReliability::Reliable), + 3 => Some(RakReliability::ReliableOrdered), + 4 => Some(RakReliability::ReliableSequenced), + 5 => Some(RakReliability::UnreliableWithAckReceipt), + 6 => Some(RakReliability::ReliableWithAckReceipt), + 7 => Some(RakReliability::ReliableOrderedWithAckReceipt), + _ => None, + } + } + + pub fn to_id(&self) -> u8 { + match self { + RakReliability::Unreliable => 0, + RakReliability::UnreliableSequenced => 1, + RakReliability::Reliable => 2, + RakReliability::ReliableOrdered => 3, + RakReliability::ReliableSequenced => 4, + RakReliability::UnreliableWithAckReceipt => 5, + RakReliability::ReliableWithAckReceipt => 6, + RakReliability::ReliableOrderedWithAckReceipt => 7, + } + } +} diff --git a/pumpkin-protocol/src/bedrock/packet_decoder.rs b/pumpkin-protocol/src/bedrock/packet_decoder.rs index 098cef8b0..e048c93fd 100644 --- a/pumpkin-protocol/src/bedrock/packet_decoder.rs +++ b/pumpkin-protocol/src/bedrock/packet_decoder.rs @@ -1,10 +1,10 @@ use std::io::Cursor; use async_compression::tokio::bufread::ZlibDecoder; -use bytes::Buf; +use bytes::Bytes; use tokio::io::{AsyncRead, AsyncReadExt, BufReader}; -use crate::{Aes128Cfb8Dec, CompressionThreshold, PacketDecodeError, RawPacket, StreamDecryptor}; +use crate::{Aes128Cfb8Dec, CompressionThreshold, PacketDecodeError, StreamDecryptor}; // decrypt -> decompress -> raw pub enum DecompressionReader { @@ -97,26 +97,16 @@ impl UDPNetworkDecoder { // take_mut::take(&mut self.reader, |decoder| decoder.upgrade(cipher)); } - pub async fn get_raw_packet( + pub async fn get_packet_payload( &mut self, mut reader: Cursor>, - ) -> Result { - // TODO: Serde is sync so we need to write to a buffer here :( - // Is there a way to deserialize in an asynchronous manner? - - let packet_id = reader - .try_get_u8() - .map_err(|_| PacketDecodeError::DecodeID)?; - + ) -> Result { let mut payload = Vec::new(); reader .read_to_end(&mut payload) .await .map_err(|err| PacketDecodeError::FailedDecompression(err.to_string()))?; - Ok(RawPacket { - id: packet_id as i32, - payload: payload.into(), - }) + Ok(payload.into()) } } diff --git a/pumpkin-protocol/src/bedrock/server/connection.rs b/pumpkin-protocol/src/bedrock/server/connection.rs deleted file mode 100644 index dcc917e21..000000000 --- a/pumpkin-protocol/src/bedrock/server/connection.rs +++ /dev/null @@ -1,10 +0,0 @@ -use pumpkin_macros::packet; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -#[packet(0x09)] -pub struct SConnectionRequest { - pub client_guid: u64, - pub time: u64, - pub security: bool, -} diff --git a/pumpkin-protocol/src/bedrock/server/mod.rs b/pumpkin-protocol/src/bedrock/server/mod.rs index b729d3e9d..8c162ec4e 100644 --- a/pumpkin-protocol/src/bedrock/server/mod.rs +++ b/pumpkin-protocol/src/bedrock/server/mod.rs @@ -1,3 +1 @@ -pub mod connection; -pub mod open_connection; -pub mod unconnected_ping; +pub mod raknet; diff --git a/pumpkin-protocol/src/bedrock/server/raknet/connection.rs b/pumpkin-protocol/src/bedrock/server/raknet/connection.rs new file mode 100644 index 000000000..b05b631b0 --- /dev/null +++ b/pumpkin-protocol/src/bedrock/server/raknet/connection.rs @@ -0,0 +1,25 @@ +use pumpkin_macros::packet; +use serde::{Deserialize, Serialize}; + +use crate::codec::socket_address::SocketAddress; + +#[derive(Serialize, Deserialize)] +#[packet(0x09)] +pub struct SConnectionRequest { + pub client_guid: u64, + pub time: u64, + pub security: bool, +} + +#[derive(Serialize, Deserialize)] +#[packet(0x13)] +pub struct SNewIncomingConnection { + pub server_address: SocketAddress, + pub internal_address: SocketAddress, + pub ping_time: u64, + pub pong_time: u64, +} + +#[derive(Serialize, Deserialize)] +#[packet(0x15)] +pub struct SDisconnect; diff --git a/pumpkin-protocol/src/bedrock/server/raknet/mod.rs b/pumpkin-protocol/src/bedrock/server/raknet/mod.rs new file mode 100644 index 000000000..b729d3e9d --- /dev/null +++ b/pumpkin-protocol/src/bedrock/server/raknet/mod.rs @@ -0,0 +1,3 @@ +pub mod connection; +pub mod open_connection; +pub mod unconnected_ping; diff --git a/pumpkin-protocol/src/bedrock/server/open_connection.rs b/pumpkin-protocol/src/bedrock/server/raknet/open_connection.rs similarity index 100% rename from pumpkin-protocol/src/bedrock/server/open_connection.rs rename to pumpkin-protocol/src/bedrock/server/raknet/open_connection.rs diff --git a/pumpkin-protocol/src/bedrock/server/unconnected_ping.rs b/pumpkin-protocol/src/bedrock/server/raknet/unconnected_ping.rs similarity index 100% rename from pumpkin-protocol/src/bedrock/server/unconnected_ping.rs rename to pumpkin-protocol/src/bedrock/server/raknet/unconnected_ping.rs diff --git a/pumpkin-protocol/src/codec/mod.rs b/pumpkin-protocol/src/codec/mod.rs index 1e4ba2d82..3724716e1 100644 --- a/pumpkin-protocol/src/codec/mod.rs +++ b/pumpkin-protocol/src/codec/mod.rs @@ -2,5 +2,6 @@ pub mod ascii_string; pub mod bit_set; pub mod item_stack_seralizer; pub mod socket_address; +pub mod u24; pub mod var_int; pub mod var_long; diff --git a/pumpkin-protocol/src/codec/u24.rs b/pumpkin-protocol/src/codec/u24.rs new file mode 100644 index 000000000..e998a26b8 --- /dev/null +++ b/pumpkin-protocol/src/codec/u24.rs @@ -0,0 +1,64 @@ +use std::io::{Read, Write}; + +use crate::ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError}; +use serde::{ + Deserialize, + de::{self, SeqAccess}, +}; + +#[derive(Clone, Copy)] +pub struct U24(pub u32); + +impl U24 { + pub fn decode(read: &mut impl Read) -> Result { + let a = read.get_u8_le()?; + let b = read.get_u8_le()?; + let c = read.get_u8_le()?; + Ok(U24(u32::from_le_bytes([a, b, c, 0]))) + } + + pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> { + let data = self.0 & 0xFFFFFF; // Get the internal u32 value + write.write_u8_be((data & 0xFF) as u8)?; + write.write_u8_be(((data >> 8) & 0xFF) as u8)?; + write.write_u8_be(((data >> 16) & 0xFF) as u8)?; + Ok(()) + } +} + +impl<'de> Deserialize<'de> for U24 { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + struct Visitor; + impl<'de> de::Visitor<'de> for Visitor { + type Value = U24; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid u24") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut data: u32 = 0; + + // Read the first byte (LSB) + data |= seq.next_element::()?.unwrap() as u32; + + // Read the second byte and shift it by 8 bits + data |= (seq.next_element::()?.unwrap() as u32) << 8; + + // Read the third next_element and shift it by 16 bits + data |= (seq.next_element::()?.unwrap() as u32) << 16; + + // Mask to ensure only the lower 24 bits are kept + Ok(U24(data & 0xFFFFFF)) + } + } + + deserializer.deserialize_seq(Visitor) + } +} diff --git a/pumpkin-protocol/src/ser/mod.rs b/pumpkin-protocol/src/ser/mod.rs index 2e3ae211e..1a4d32308 100644 --- a/pumpkin-protocol/src/ser/mod.rs +++ b/pumpkin-protocol/src/ser/mod.rs @@ -3,7 +3,7 @@ use std::io::{Read, Write}; use crate::{ FixedBitSet, - codec::{bit_set::BitSet, var_int::VarInt, var_long::VarLong}, + codec::{bit_set::BitSet, u24::U24, var_int::VarInt, var_long::VarLong}, }; pub mod deserializer; @@ -48,6 +48,8 @@ pub enum WritingError { pub trait NetworkReadExt { fn get_i8_be(&mut self) -> Result; fn get_u8_be(&mut self) -> Result; + fn get_u8_le(&mut self) -> Result; + fn get_i16_be(&mut self) -> Result; fn get_u16_be(&mut self) -> Result; fn get_i32_be(&mut self) -> Result; @@ -63,6 +65,7 @@ pub trait NetworkReadExt { fn read_remaining_to_boxed_slice(&mut self, bound: usize) -> Result, ReadingError>; fn get_bool(&mut self) -> Result; + fn get_u24(&mut self) -> Result; fn get_var_int(&mut self) -> Result; fn get_var_long(&mut self) -> Result; fn get_string_bounded(&mut self, bound: usize) -> Result; @@ -100,6 +103,14 @@ impl NetworkReadExt for R { Ok(u8::from_be_bytes(buf)) } + fn get_u8_le(&mut self) -> Result { + let mut buf = [0u8]; + self.read_exact(&mut buf) + .map_err(|err| ReadingError::Incomplete(err.to_string()))?; + + Ok(u8::from_le_bytes(buf)) + } + fn get_i16_be(&mut self) -> Result { let mut buf = [0u8; 2]; self.read_exact(&mut buf) @@ -116,6 +127,10 @@ impl NetworkReadExt for R { Ok(u16::from_be_bytes(buf)) } + fn get_u24(&mut self) -> Result { + U24::decode(self) + } + fn get_i32_be(&mut self) -> Result { let mut buf = [0u8; 4]; self.read_exact(&mut buf) @@ -289,6 +304,7 @@ pub trait NetworkWriteExt { fn write_u8_be(&mut self, data: u8) -> Result<(), WritingError>; fn write_i16_be(&mut self, data: i16) -> Result<(), WritingError>; fn write_u16_be(&mut self, data: u16) -> Result<(), WritingError>; + fn write_u24_be(&mut self, data: U24) -> Result<(), WritingError>; fn write_i32_be(&mut self, data: i32) -> Result<(), WritingError>; fn write_u32_be(&mut self, data: u32) -> Result<(), WritingError>; fn write_i64_be(&mut self, data: i64) -> Result<(), WritingError>; @@ -371,6 +387,10 @@ impl NetworkWriteExt for W { .map_err(WritingError::IoError) } + fn write_u24_be(&mut self, data: U24) -> Result<(), WritingError> { + data.encode(self) + } + fn write_i32_be(&mut self, data: i32) -> Result<(), WritingError> { self.write_all(&data.to_be_bytes()) .map_err(WritingError::IoError) diff --git a/pumpkin/src/net/bedrock/connection.rs b/pumpkin/src/net/bedrock/connection.rs new file mode 100644 index 000000000..73cac86cd --- /dev/null +++ b/pumpkin/src/net/bedrock/connection.rs @@ -0,0 +1,34 @@ +use std::{net::SocketAddr, time::UNIX_EPOCH}; + +use pumpkin_protocol::{ + bedrock::{ + RakReliability, client::raknet::connection::CConnectionRequestAccepted, + server::raknet::connection::SConnectionRequest, + }, + codec::socket_address::SocketAddress, +}; + +use crate::net::{Client, bedrock::BedrockClientPlatform}; + +impl Client { + pub async fn handle_connection_request( + &self, + bedrock: &BedrockClientPlatform, + packet: SConnectionRequest, + ) { + dbg!("send connection accepted"); + bedrock + .send_framed_packet( + self, + &CConnectionRequestAccepted::new( + SocketAddress(*self.address.lock().await), + 0, + vec![], + packet.time, + UNIX_EPOCH.elapsed().unwrap().as_millis() as u64, + ), + RakReliability::Unreliable, + ) + .await; + } +} diff --git a/pumpkin/src/net/bedrock/mod.rs b/pumpkin/src/net/bedrock/mod.rs index 4ad1ef650..270024435 100644 --- a/pumpkin/src/net/bedrock/mod.rs +++ b/pumpkin/src/net/bedrock/mod.rs @@ -1,25 +1,34 @@ use std::{ io::{Cursor, Write}, - sync::Arc, + sync::{ + Arc, + atomic::{AtomicBool, AtomicU32, Ordering}, + }, }; use bytes::Bytes; use pumpkin_protocol::{ - ClientPacket, PacketDecodeError, PacketEncodeError, RawPacket, ServerPacket, + ClientPacket, PacketDecodeError, PacketEncodeError, ServerPacket, bedrock::{ + RAKNET_ACK, RAKNET_NACK, RAKNET_VALID, RakReliability, + ack::Ack, + frame_set::{Frame, FrameSet}, packet_decoder::UDPNetworkDecoder, packet_encoder::UDPNetworkEncoder, - server::{ + server::raknet::{ + connection::{SConnectionRequest, SDisconnect}, open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2}, unconnected_ping::SUnconnectedPing, }, }, + codec::u24::U24, packet::Packet, - ser::{NetworkWriteExt, ReadingError, WritingError}, + ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError}, }; use std::net::SocketAddr; use tokio::{net::UdpSocket, sync::Mutex}; +pub mod connection; pub mod open_connection; pub mod unconnected; @@ -33,6 +42,10 @@ pub struct BedrockClientPlatform { network_writer: Arc>, /// The packet decoder for incoming packets. network_reader: Mutex, + + use_frame_sets: AtomicBool, + output_sequence: AtomicU32, + output_reliable_index: AtomicU32, } impl BedrockClientPlatform { @@ -43,19 +56,18 @@ impl BedrockClientPlatform { addr, network_writer: Arc::new(Mutex::new(UDPNetworkEncoder::new())), network_reader: Mutex::new(UDPNetworkDecoder::new()), + use_frame_sets: AtomicBool::new(false), + output_sequence: AtomicU32::new(0), + output_reliable_index: AtomicU32::new(0), } } pub async fn process_packet(&self, client: &Client, server: &Server, packet: Cursor>) { - let packet = self.get_packet(client, packet).await; + let packet = self.get_packet_payload(client, packet).await; if let Some(packet) = packet { - if let Err(error) = Self::handle_packet(client, server, &packet).await { + if let Err(error) = self.handle_packet_payload(client, server, packet).await { let _text = format!("Error while reading incoming packet {error}"); - log::error!( - "Failed to read incoming packet with id {}: {}", - packet.id, - error - ); + log::error!("Failed to read incoming packet with : {error}"); //self.kick(TextComponent::text(text)).await; } } @@ -78,12 +90,34 @@ impl BedrockClientPlatform { .await } - pub async fn send_packet_now(&self, client: &Client, packet: Vec) { + pub async fn send_framed_packet( + &self, + client: &Client, + packet: &P, + reliability: RakReliability, + ) { + let mut packet_buf = Vec::new(); + let writer = &mut packet_buf; + Self::write_packet(packet, writer).unwrap(); + let frame = Frame { + payload: packet_buf.into(), + reliability, + reliable_index: self.output_reliable_index.fetch_add(1, Ordering::Relaxed), + ..Default::default() + }; + + // TODO: this is really bad, batch this + let frame_set = FrameSet { + sequence: U24(self.output_sequence.fetch_add(1, Ordering::Relaxed)), + frames: vec![frame], + }; + let mut packet_buf = Vec::new(); + Self::write_packet(&frame_set, &mut packet_buf).unwrap(); if let Err(err) = self .network_writer .lock() .await - .write_packet(packet.into(), self.addr, &self.socket) + .write_packet(packet_buf.into(), self.addr, &self.socket) .await { // It is expected that the packet will fail if we are closed @@ -96,13 +130,124 @@ impl BedrockClientPlatform { } } - pub async fn handle_packet( + pub async fn send_packet_now(&self, client: &Client, packet: Vec) { + if !self.use_frame_sets.load(Ordering::Relaxed) { + // Sent the packet directly + if let Err(err) = self + .network_writer + .lock() + .await + .write_packet(packet.into(), self.addr, &self.socket) + .await + { + // It is expected that the packet will fail if we are closed + if !client.closed.load(std::sync::atomic::Ordering::Relaxed) { + log::warn!("Failed to send packet to client {}: {}", client.id, err); + // We now need to close the connection to the client since the stream is in an + // unknown state + client.close(); + } + } + } + } + + pub async fn handle_packet_payload( + &self, client: &Client, server: &Server, - packet: &RawPacket, + packet: Bytes, ) -> Result<(), ReadingError> { - let payload = &packet.payload[..]; - match packet.id { + let mut payload = &packet[..]; + + let Ok(id) = payload.get_u8_be() else { + return Err(ReadingError::CleanEOF(String::new())); + }; + + let is_valid = id & RAKNET_VALID == RAKNET_VALID; + if !is_valid { + // Offline packets just have Packet ID + Payload + return Self::handle_offline_packet(client, server, i32::from(id), payload).await; + } + self.use_frame_sets.store(true, Ordering::Relaxed); + let header = id; + + match header { + RAKNET_ACK => { + dbg!("received ack"); + } + RAKNET_NACK => { + dbg!("received non ack"); + } + 0x80..0x8d => { + self.handle_frame_set(client, server, FrameSet::read(payload)?) + .await; + } + _ => { + log::warn!("Received unknown online packet {header}"); + } + } + Ok(()) + } + + async fn handle_frame_set(&self, client: &Client, server: &Server, frame_set: FrameSet) { + // TODO: this is bad + client + .send_packet_now(&Ack::new(vec![frame_set.sequence.0])) + .await; + // TODO + for frame in frame_set.frames { + self.handle_frame(client, server, &frame).await.unwrap(); + } + } + + async fn handle_frame( + &self, + client: &Client, + server: &Server, + frame: &Frame, + ) -> Result<(), ReadingError> { + if frame.split_size > 0 { + dbg!("oh no, frame is split, TODO"); + } + dbg!(frame.reliability); + + let mut payload = &frame.payload[..]; + let id = payload.get_u8_be()?; + self.handle_packet(client, server, i32::from(id), payload) + .await + } + + async fn handle_packet( + &self, + client: &Client, + _server: &Server, + packet_id: i32, + payload: &[u8], + ) -> Result<(), ReadingError> { + match packet_id { + SConnectionRequest::PACKET_ID => { + client + .handle_connection_request(self, SConnectionRequest::read(payload)?) + .await; + } + SDisconnect::PACKET_ID => { + dbg!("Bedrock client disconnected"); + client.close(); + } + _ => { + log::warn!("Received Online online packet {packet_id}"); + } + } + Ok(()) + } + + async fn handle_offline_packet( + client: &Client, + server: &Server, + packet_id: i32, + payload: &[u8], + ) -> Result<(), ReadingError> { + match packet_id { SUnconnectedPing::PACKET_ID => { client .handle_unconnected_ping(server, SUnconnectedPing::read(payload)?) @@ -119,20 +264,24 @@ impl BedrockClientPlatform { .await; } _ => { - log::error!("Failed to handle bedrock client packet id {}", packet.id); + log::error!("Failed to handle bedrock client packet id {packet_id}"); } } Ok(()) } - pub async fn get_packet(&self, client: &Client, packet: Cursor>) -> Option { + pub async fn get_packet_payload( + &self, + client: &Client, + packet: Cursor>, + ) -> Option { let mut network_reader = self.network_reader.lock().await; tokio::select! { () = client.await_close_interrupt() => { log::debug!("Canceling player packet processing"); None }, - packet_result = network_reader.get_raw_packet(packet) => { + packet_result = network_reader.get_packet_payload(packet) => { match packet_result { Ok(packet) => Some(packet), Err(err) => { diff --git a/pumpkin/src/net/bedrock/open_connection.rs b/pumpkin/src/net/bedrock/open_connection.rs index 61ea806a7..ee5670270 100644 --- a/pumpkin/src/net/bedrock/open_connection.rs +++ b/pumpkin/src/net/bedrock/open_connection.rs @@ -1,7 +1,7 @@ use pumpkin_protocol::{ bedrock::{ - client::open_connection::{COpenConnectionReply1, COpenConnectionReply2}, - server::open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2}, + client::raknet::open_connection::{COpenConnectionReply1, COpenConnectionReply2}, + server::raknet::open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2}, }, codec::socket_address::SocketAddress, }; diff --git a/pumpkin/src/net/bedrock/unconnected.rs b/pumpkin/src/net/bedrock/unconnected.rs index c949c5921..c8a8addc9 100644 --- a/pumpkin/src/net/bedrock/unconnected.rs +++ b/pumpkin/src/net/bedrock/unconnected.rs @@ -1,8 +1,8 @@ use pumpkin_config::BASIC_CONFIG; use pumpkin_protocol::{ bedrock::{ - client::unconnected_pong::{CUnconnectedPong, ServerInfo}, - server::unconnected_ping::SUnconnectedPing, + client::raknet::unconnected_pong::{CUnconnectedPong, ServerInfo}, + server::raknet::unconnected_ping::SUnconnectedPing, }, codec::ascii_string::AsciiString, }; @@ -14,8 +14,8 @@ impl Client { let motd_string = ServerInfo { edition: "MCPE", motd_line_1: &BASIC_CONFIG.motd, - protocol_version: 527, - version_name: "1.19.1", + protocol_version: 818, + version_name: "1.21.90", player_count: 1, max_player_count: BASIC_CONFIG.max_players, server_unique_id: server.server_guid,