From 4b409e875bec5a679e95ef590d7dbcd1a648a45c Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 22 Oct 2022 23:21:11 -0500 Subject: [PATCH 01/75] feat: v3 begins --- README.md | 49 ++- src/conn/mod.rs | 20 + src/{internal => conn}/queue.rs | 119 ++--- src/connection/conn.rs | 282 ------------ src/connection/mod.rs | 7 - src/connection/state.rs | 89 ---- src/internal/ack/ack.rs | 139 ------ src/internal/ack/mod.rs | 3 - src/internal/frame/fragment.rs | 14 - src/internal/frame/mod.rs | 312 -------------- src/internal/frame/reliability/cache.rs | 53 --- src/internal/frame/reliability/mod.rs | 97 ----- src/internal/handler.rs | 549 ------------------------ src/internal/mod.rs | 25 -- src/internal/util.rs | 15 - src/lib.rs | 30 +- src/protocol/mcpe/mod.rs | 12 - src/protocol/mcpe/motd.rs | 204 --------- src/protocol/mcpe/packet.rs | 15 - src/protocol/mod.rs | 9 - src/protocol/packet/handler.rs | 147 ------- src/protocol/packet/mod.rs | 352 --------------- src/protocol/packet/offline.rs | 133 ------ src/protocol/packet/online.rs | 134 ------ src/protocol/util.rs | 33 -- src/server/mod.rs | 25 +- src/server/motd.rs | 3 + src/server/std.rs | 1 - src/server/tokio.rs | 357 --------------- test/Cargo.toml | 11 - test/src/main.rs | 43 -- tests/defaults.rs | 7 - tests/mod.rs | 1 - 33 files changed, 134 insertions(+), 3156 deletions(-) create mode 100644 src/conn/mod.rs rename src/{internal => conn}/queue.rs (60%) delete mode 100644 src/connection/conn.rs delete mode 100644 src/connection/mod.rs delete mode 100644 src/connection/state.rs delete mode 100644 src/internal/ack/ack.rs delete mode 100644 src/internal/ack/mod.rs delete mode 100644 src/internal/frame/fragment.rs delete mode 100644 src/internal/frame/mod.rs delete mode 100644 src/internal/frame/reliability/cache.rs delete mode 100644 src/internal/frame/reliability/mod.rs delete mode 100644 src/internal/handler.rs delete mode 100644 src/internal/mod.rs delete mode 100644 src/internal/util.rs delete mode 100644 src/protocol/mcpe/mod.rs delete mode 100644 src/protocol/mcpe/motd.rs delete mode 100644 src/protocol/mcpe/packet.rs delete mode 100644 src/protocol/mod.rs delete mode 100644 src/protocol/packet/handler.rs delete mode 100644 src/protocol/packet/mod.rs delete mode 100644 src/protocol/packet/offline.rs delete mode 100644 src/protocol/packet/online.rs delete mode 100644 src/protocol/util.rs create mode 100644 src/server/motd.rs delete mode 100644 src/server/std.rs delete mode 100644 src/server/tokio.rs delete mode 100644 test/Cargo.toml delete mode 100644 test/src/main.rs delete mode 100644 tests/defaults.rs delete mode 100644 tests/mod.rs diff --git a/README.md b/README.md index f92bce9..abff8bb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,50 @@ # RakNet -A fully functional RakNet implementation in rust, asynchronously driven. \ No newline at end of file +A fully functional RakNet implementation in rust, asynchronously driven. + + +```rust +// Create a server +use raknet::Listener; +use raknet::util::handle; +use raknet::util::mcpe; + +async fn my_handler(conn: RakConnection, mut stream: RakStream) { + // The `conn.recv()` method constructs a `Packet` from the stream + // Which becomes usable later. + while let Some(packet) = conn.recv(&mut stream).await { + if let Ok(packet) = packet { + // RakNet packets are sent here! We can send some back as well! + let hello = raknet::protocol::online::ConnectedPing::new(); + conn.send(hello.into(), Reliability::Reliable).await; + } + } +} + +async fn main() { + // Bind to a socket and allow minecraft protocol + let mut server = Listener::new("0.0.0.0:19132", true).await; + server.motd = Motd::new( + "Rust Bedrock Minecraft server", + // 100 players, maximum + 100, + // The minecraft version to display + "1.18.0", + // The Gamemode to display + mcpe::Gamemode::Creative + ); + + // Begin listening to incoming connections + server.start().await; + + loop { + let (conn, stream, _) = server.accept().await.unwrap(); + + // You can use the default handler, or create your own + // the default handler + tokio::spawn(handle(conn, stream)); + // your own handler + tokio::spawn(my_handler(conn, stream)); + } +} +``` \ No newline at end of file diff --git a/src/conn/mod.rs b/src/conn/mod.rs new file mode 100644 index 0000000..0e39d0a --- /dev/null +++ b/src/conn/mod.rs @@ -0,0 +1,20 @@ +pub mod queue; + +use std::{net::SocketAddr, sync::Arc}; + +use tokio::sync::RwLock; + +use self::queue::SendQueue; + +/// This struct is utilized internally and represented +/// as per each "connection" or "socket" to the server. +/// Each Connection has it's own Reference pointer to a +/// socket dedicated to this connection. +pub struct Conn { + /// The address of the connection + /// This is internally tokenized by rak-rs + pub address: SocketAddr, + + /// The queue used to send packets back to the connection. + pub(crate) send_queue: Arc>, +} \ No newline at end of file diff --git a/src/internal/queue.rs b/src/conn/queue.rs similarity index 60% rename from src/internal/queue.rs rename to src/conn/queue.rs index 4297524..a4d8d76 100644 --- a/src/internal/queue.rs +++ b/src/conn/queue.rs @@ -1,79 +1,28 @@ use std::collections::HashMap; -/// A packet queue, this is used to store packets that are waiting to be sent. -/// This is internal use for Sessions. - -#[derive(Debug, Clone)] -pub struct Queue { - /// Normal priority packet. - /// This is the default priority. - normal: Vec, - /// Lowest priority packet. - /// This is the lowest priority. - low: Vec, - /// Whether or not the queue is frozen. - pub frozen: bool, -} - -impl Queue { - pub fn new() -> Self { - Queue { - normal: Vec::new(), - low: Vec::new(), - frozen: false, - } - } - - /// Pushes a packet to the queue. - /// Note that packets of high priority will be ignored - pub fn push(&mut self, packet: T, priority: SendPriority) { - if self.frozen { - return; - } - match priority { - SendPriority::Normal => self.normal.push(packet), - SendPriority::Low => self.low.push(packet), - SendPriority::Immediate => return, - } - } - - pub fn flush_low(&mut self) -> Vec { - let mut low = Vec::new(); - std::mem::swap(&mut low, &mut self.low); - low - } - - pub fn flush_normal(&mut self) -> Vec { - let mut normal = Vec::new(); - std::mem::swap(&mut normal, &mut self.normal); - normal - } - - pub fn flush(&mut self) -> Vec { - let mut normal = self.flush_normal(); - let mut low = self.flush_low(); - normal.append(&mut low); - return normal; - } - - pub fn len(self) -> usize { - self.normal.len() + self.low.len() - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SendPriority { - /// The packet needs to be sent as fast as possible. - /// Packets with this priority are sent immediately. - Immediate, - /// The packet needs to be sent, but is not as important as High priority. - /// Packets with this priority will be batched together in frames. - /// This is the default priority. - Normal, - /// The packet being sent does not need to be reliably sent, packets with this priority are sent - /// last. (Don't use this for MCPE packets) - Low, -} +/// An ordered queue is used to Index incoming packets over a channel +/// within a reliable window time. +/// +/// Usage: +/// ```rust no_run +/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); +/// // Insert a packet with the id of "1" +/// ord_qu.insert(vec![0, 1], 1); +/// ord_qu.insert(vec![1, 0], 5); +/// ord_qu.insert(vec![2, 0], 3); +/// +/// // Get the packets we still need. +/// let needed: Vec = ord.qu.flush_missing(); +/// assert_eq!(needed, vec![2, 4]); +/// +/// // We would in theory, request these packets, but we're going to insert them +/// ord_qu.insert(vec![2, 0, 0, 1], 4); +/// ord_qu.insert(vec![1, 0, 0, 2], 2); +/// +/// // Now let's return our packets in order. +/// // Will return a vector of these packets in order by their "id". +/// let ordered: Vec> = ord.qu.flush(); +/// ``` #[derive(Debug)] pub struct OrderedQueue { /// The queue of packets that are in order. Mapped to the time they were received. @@ -173,3 +122,25 @@ where self.scope.1 - self.scope.0 } } + +/// This queue is used to prioritize packets being sent out +/// Packets that are old, are either dropped or requested again, +/// you can define this behavior with the `timeout` property. +/// ! This API replaces `CacheStore` for ack channels +#[derive(Debug, Clone)] +pub struct SendQueue { + /// The amount of time that needs to pass for a packet to be + /// dropped or requested again. + timeout: u16, + + /// The amount of times we should retry sending a packet before + /// dropping it from the queue. This is currently set to `5`. + max_tries: u16, + + /// The current sequence number. This is incremented every time + /// a packet is sent reliably. We can resend these if they are + /// Acked. + send_seq: u32, + + +} diff --git a/src/connection/conn.rs b/src/connection/conn.rs deleted file mode 100644 index 00417e6..0000000 --- a/src/connection/conn.rs +++ /dev/null @@ -1,282 +0,0 @@ -use binary_utils::*; -use std::{collections::VecDeque, sync::Arc, time::SystemTime}; - -use crate::{ - internal::{ - frame::reliability::Reliability, - queue::{Queue, SendPriority}, - RakConnHandler, RakConnHandlerMeta, - }, - protocol::{mcpe::motd::Motd, online::Disconnect, Packet}, - rak_debug, - server::{RakEvent, RakNetVersion}, -}; - -use crate::protocol::handler::{handle_offline, handle_online}; - -use super::state::ConnectionState; - -pub type SendCommand = (String, Vec); - -#[derive(Debug, Clone)] -pub struct Connection { - /// The tokenized address of the connection. - /// This is the identifier rak-rs will use to identify the connection. - /// It follows the format `:`. - pub address: String, - /// The current state of the connection. - /// This is used to determine what packets can be sent and at what times. - /// Some states are used internally to rak-rs, but are not used in actual protocol - /// such as "Unidentified" and "Online". - pub state: ConnectionState, - /// The maximum transfer unit for the connection. - /// Any outbound packets will be sharded into frames of this size. - /// By default minecraft will use `1400` bytes. However raknet has 16 bytes of overhead. - /// so this may be reduced as `1400 - 16` which is `1384`. - pub mtu: u16, - /// The last recieved time. - /// This is used to determine if the connection has timed out. - /// This is the time the last packet was recieved. - pub recv_time: SystemTime, - /// The time the server started. - /// Used in pings - pub start_time: SystemTime, - /// The RakNet Version of the server. - /// This is used to determine if the player can reliably join the server. - pub raknet_version: RakNetVersion, - /// Minecraft specific, the message of the day. - pub motd: Motd, - /// A reference to the server id. - pub server_guid: u64, - /// The packet queue for the connection. - /// This is used to store packets that need to be sent, any packet here **WILL** be batched! - pub queue: Queue>, - /// This is an internal channel used on the raknet side to send packets to the user immediately. - /// DO NOT USE THIS! - pub send_channel: Arc>, - /// This is internal! This is used to dispatch events to the user. - /// This will probably change in the near future, however this will stay, - /// until that happens. - pub event_dispatch: VecDeque, - /// This is internal! This is used to handle all raknet packets, like frame, ping etc. - pub(crate) rakhandler: RakConnHandlerMeta, - /// This is internal! This is used to remove the connection if something goes wrong with connection states. - /// (which is likely) - ensure_disconnect: bool, -} - -impl Connection { - pub fn new( - address: String, - send_channel: Arc>, - start_time: SystemTime, - server_guid: u64, - port: String, - raknet_version: RakNetVersion, - ) -> Self { - Self { - address, - state: ConnectionState::Unidentified, - mtu: 1400, - recv_time: SystemTime::now(), - start_time, - motd: Motd::new(server_guid, port), - server_guid, - queue: Queue::new(), - send_channel, - event_dispatch: VecDeque::new(), - raknet_version, - ensure_disconnect: false, - rakhandler: RakConnHandlerMeta::new(), - } - } - - /// Get the maximum allowed size of a entire frame packet. - /// This is the MTU - the size of all possible raknet headers, - /// so: `40 (Datagram Protocol) + 20 (Raknet)` - pub fn max_frame_size(&self) -> usize { - self.mtu as usize - 60 - } - - /// Adds the given stream to the connection's queue by priority. - /// If instant is set to "true" the packet will be sent immediately. - pub fn send(&mut self, stream: Vec, instant: bool) { - if instant { - // We're not going to batch this packet, so send it immediately. - self.send_immediate(stream); - } else { - // We're going to batch this packet, so push it to the queue. - self.queue.push(stream, SendPriority::Normal); - } - } - - /// This method should be used externally to send packets to the connection. - /// Packets here will be batched together and sent in frames. - pub fn send_stream(&mut self, stream: Vec, priority: SendPriority) { - if priority == SendPriority::Immediate { - RakConnHandler::send_framed(self, stream, Reliability::ReliableOrd); - } else { - self.queue.push(stream, priority); - } - } - - /// Immediately send the packet to the connection. - /// This will not automatically batch the packet. - pub fn send_immediate(&mut self, stream: Vec) { - // check the context - if let Ok(_) = - futures_executor::block_on(self.send_channel.send((self.address.clone(), stream))) - { - // GREAT! - } else { - rak_debug!("Failed to send packet to {}", self.address); - } - } - - /// Sends the packet inside a frame and may queue it based on priority. - /// All sent packets will be sent in reliably ordered frames. - /// - /// WARNING: DO NOT USE THIS FOR PACKETS THAT EXCEED MTU SIZE! - pub fn send_frame(&mut self, stream: Vec, priority: SendPriority) { - if priority == SendPriority::Immediate { - // we need to batch this frame immediately. - RakConnHandler::send_framed(self, stream, Reliability::ReliableOrd); - } else { - // we need to batch this frame. - self.queue.push(stream, priority); - } - } - - /// This will send a raknet packet to the connection. - /// This method will automatically parse the packet and send it by the given priority. - pub fn send_packet(&mut self, packet: Packet, priority: SendPriority) { - // we can check the kind, if it's an online packet we need to frame it. - if packet.is_online() { - self.send_frame(packet.parse().unwrap(), priority); - return; - } - - if priority == SendPriority::Immediate { - self.send_immediate(packet.parse().unwrap()); - } else { - self.queue - .push(packet.parse().unwrap(), SendPriority::Normal); - } - } - - pub fn recv(&mut self, payload: &Vec) { - self.recv_time = SystemTime::now(); - - // build the packet - if let Ok(packet) = Packet::compose(&payload, &mut 0) { - // the packet is internal, let's check if it's an online packet or offline packet - // and handle it accordingly. - if packet.is_online() { - // we recieved a frame packet out of scope. - // return an error. - return; - } else { - // offline packet - // handle the disconnected packet - handle_offline(self, packet); - - // let's verify our state. - if !self.state.is_reliable() { - // we got a packet when the client state was un-reliable, we're going to force the client - // to un-identified. - self.state = ConnectionState::Unidentified; - } - } - } else { - // this packet could be a Ack or Frame - // lets pass it to the rak handler. The rakhandler will invoke `connection.handle` which is - // where we handle the online packets. - if let Err(e) = RakConnHandler::handle(self, payload) { - rak_debug!( - "We got a packet that we couldn't parse! Probably a Nak or Frame! Error: {}", - e - ); - } - - // let's update the client state to connected. - if !self.state.is_reliable() { - self.state = ConnectionState::Connected; - } - } - } - - /// This is called by the rak handler when each frame is decoded. - /// These packets are usually online packets or game packets! - pub(crate) fn handle(&mut self, buffer: Vec) { - // check if the payload is a online packet. - if let Ok(packet) = Packet::compose(&buffer, &mut 0) { - // this is a packet! let's check the variety. - if packet.is_online() { - // online packet - // handle the online packet - if let Err(_) = handle_online(self, packet.clone()) { - // unknown packet lol - rak_debug!("Unknown packet! {:#?}", packet); - } - } else { - // offline packet, - // we can't handle offline packets sent within a frame. - // we need to handle them in the `connection.recv` method. - // we're going to force the client to be disconnected as this is not a valid packet. - self.disconnect("Incorrect protocol usage within raknet.", true); - } - } else { - // this isn't an online packet we know about, so we're going to emit an event here. - // this is probably a game packet. - self.event_dispatch - .push_back(RakEvent::GamePacket(self.address.clone(), buffer)); - } - } - - pub fn disconnect>(&mut self, reason: S, server_initiated: bool) { - // disconnect!!! - self.event_dispatch - .push_back(RakEvent::Disconnect(self.address.clone(), reason.into())); - // actually handle this internally, cause we can't send packets if we're disconnected. - self.state = ConnectionState::Offline; - // the following is a hack to make sure the connection is removed from the server. - self.ensure_disconnect = true; - // We also need to flush the queue so packets aren't sent, because they are now useless. - self.queue.flush(); - // Freeze the queue, just in case this is a server sided disconnect. - // Otherwise this is useless. - self.queue.frozen = true; - - if server_initiated { - self.send_packet(Disconnect {}.into(), SendPriority::Immediate); - } - } - - /// This reads an internal value! This may not be in relation to the client's CURRENT state! - pub fn is_disconnected(&self) -> bool { - return self.ensure_disconnect == true; - } - - /// This is called every RakNet tick. - /// This is used to update the connection state and send `Priority::Normal` packets. - /// as well as other internal stuff like updating flushing Ack and Nack. - pub fn tick(&mut self) { - if self.state.is_reliable() { - // we need to update the state of the connection. - // check whether or not we're becoming un-reliable. - if self.recv_time.elapsed().unwrap().as_secs() > 8 { - // we're becoming un-reliable. - self.state = ConnectionState::TimingOut; - } - // tick the rakhandler - RakConnHandler::tick(self); - } else { - if self.recv_time.elapsed().unwrap().as_secs() >= 15 { - // we're not reliable anymore. - self.state = ConnectionState::Disconnected; - self.disconnect("Timed Out", true); - return; - } - } - } -} diff --git a/src/connection/mod.rs b/src/connection/mod.rs deleted file mode 100644 index 009acd5..0000000 --- a/src/connection/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// The actual connection. -pub mod conn; - -/// Connection states -pub mod state; - -pub use self::conn::*; diff --git a/src/connection/state.rs b/src/connection/state.rs deleted file mode 100644 index b1c422a..0000000 --- a/src/connection/state.rs +++ /dev/null @@ -1,89 +0,0 @@ -/// Connection States -/// These are all possible states of a raknet session. -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum ConnectionState { - /// The Session is not yet connected, but is actively trying to connect. - /// Clients in this state are considered to be actively trying to connect. - Connecting, - - /// The Session is connected and ready to send and receive packets. - /// This is the state after a connection has been established. - /// - /// This state is applied once the `ConnectionHandshake` has been completed. - Connected, - - /// The session is being timed out because it has not sent a packet in a while. - /// The interval for this can be set in the Session Options. - TimingOut, - - /// The session has been disconnected but is still in the process of cleaning up. - /// This is the state after a disconnect has been requested, but the client still wants - /// to send packets until its done. - Disconnecting, - - /// The session has been disconnected and is ready to be removed. - /// This is the state after a disconnect has been requested and the client has - /// This is almost never used. - Disconnected, - - /// The session is replying to the server but is not actually connected. This is - /// the state where ping and pong packets are being sent. - Unidentified, - - /// The session is not connected and is not trying to connect. - /// During this state the session will be dropped. This state occurs when a client - /// has completely stopped responding to packets or their socket is destroyed. - Offline, -} - -impl ConnectionState { - /// Returns whether or not the Session is reliable. - /// Reliable sessions are sessions that are not: - /// - Offline - /// - Disconnected - /// - TimingOut - pub fn is_reliable(&self) -> bool { - match self { - Self::Disconnected | Self::TimingOut | Self::Offline => false, - _ => true, - } - } - - /// Returns whether or not the Session is available to recieve - /// packets. Sessions in this state are: - /// - Connected - /// - Connecting - /// - Unidentified - /// - Disconnecting - pub fn is_available(&self) -> bool { - match self { - Self::Connected | Self::Connecting | Self::Unidentified | Self::Disconnecting => true, - _ => false, - } - } - - /// Returns whether or not the Session is in any "connected" state. - /// Sessions in this state are: - /// - Connected - /// - Connecting - pub fn is_connected(&self) -> bool { - match self { - Self::Connected | Self::Connecting => true, - _ => false, - } - } -} - -impl std::fmt::Display for ConnectionState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Connecting => write!(f, "Connecting"), - Self::Connected => write!(f, "Connected"), - Self::TimingOut => write!(f, "TimingOut"), - Self::Disconnecting => write!(f, "Disconnecting"), - Self::Disconnected => write!(f, "Disconnected"), - Self::Unidentified => write!(f, "Unidentified"), - Self::Offline => write!(f, "Offline"), - } - } -} diff --git a/src/internal/ack/ack.rs b/src/internal/ack/ack.rs deleted file mode 100644 index b3b1dd2..0000000 --- a/src/internal/ack/ack.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{io::Cursor, ops::Range}; - -use binary_utils::Streamable; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; - -/// An ack record. -/// A record holds a single or range of acked packets. -/// No real complexity other than that. -#[derive(Debug, Clone)] -pub enum Record { - Single(SingleRecord), - Range(RangeRecord), -} - -#[derive(Debug, Clone)] -pub struct SingleRecord { - pub sequence: u32, -} - -#[derive(Debug, Clone)] -pub struct RangeRecord { - pub start: u32, - pub end: u32, -} - -impl RangeRecord { - /// Fixes the end of the range if it is lower than the start. - pub fn fix(&mut self) { - if self.end < self.start { - let temp = self.end; - self.end = self.start; - self.start = temp; - } - } -} - -#[derive(Debug, Clone)] -pub struct Ack { - pub id: u8, - pub count: u16, - pub records: Vec, -} - -impl Ack { - pub fn new(count: u16, nack: bool) -> Self { - Self { - id: if nack { 0xa0 } else { 0xc0 }, - count, - records: Vec::new(), - } - } - - pub fn push_record(&mut self, seq: u32) { - self.records - .push(Record::Single(SingleRecord { sequence: seq })); - } - - pub fn from_missing(missing: Vec) -> Self { - let mut records: Vec = Vec::new(); - let mut current: Range = 0..0; - - for m in missing { - if current.end + 1 == m { - current.end += 1; - } else if m > current.end { - // This is a new range. - records.push(Record::Range(RangeRecord { - start: current.start, - end: current.end, - })); - current.start = m; - current.end = m; - } else { - // This is a new single. - records.push(Record::Single(SingleRecord { sequence: m })); - current.start = m + 1; - current.end = m + 1; - } - } - - let mut nack = Self::new(records.len().try_into().unwrap(), true); - nack.records = records; - - return nack; - } -} - -impl Streamable for Ack { - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut stream: Vec = Vec::new(); - stream.push(self.id); - stream.write_u16::(self.count)?; - - for record in self.records.iter() { - match record { - Record::Single(rec) => { - stream.push(1); - stream.write_u24::(rec.sequence)?; - } - Record::Range(rec) => { - stream.push(0); - stream.write_u24::(rec.start)?; - stream.write_u24::(rec.end)?; - } - } - } - Ok(stream) - } - - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let mut stream = Cursor::new(source); - let id = stream.read_u8().unwrap(); - let count = stream.read_u16::().unwrap(); - let mut records: Vec = Vec::new(); - for _ in 0..count { - if stream.read_u8().unwrap() == 1 { - let record: SingleRecord = SingleRecord { - sequence: stream.read_u24::().unwrap(), - }; - - records.push(Record::Single(record)); - } else { - let record: RangeRecord = RangeRecord { - start: stream.read_u24::().unwrap(), - end: stream.read_u24::().unwrap(), - }; - - records.push(Record::Range(record)); - } - } - - *position += stream.position() as usize; - - Ok(Self { count, records, id }) - } -} diff --git a/src/internal/ack/mod.rs b/src/internal/ack/mod.rs deleted file mode 100644 index c90d5ea..0000000 --- a/src/internal/ack/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod ack; - -pub use self::ack::*; diff --git a/src/internal/frame/fragment.rs b/src/internal/frame/fragment.rs deleted file mode 100644 index 37fa5a1..0000000 --- a/src/internal/frame/fragment.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// The information for the given fragment. -/// This is used to determine how to reassemble the frame. -#[derive(Debug, Clone)] -pub struct FragmentMeta { - /// The total number of fragments in this frame. - pub(crate) size: u32, - /// The identifier for this fragment. - /// This is used similar to a ordered channel, where the trailing buffer - /// will be stored with this identifier. - pub(crate) id: u16, - /// The index of the fragment. - /// This is the arrangement of the fragments in the frame. - pub(crate) index: u32, -} diff --git a/src/internal/frame/mod.rs b/src/internal/frame/mod.rs deleted file mode 100644 index 6ef7bca..0000000 --- a/src/internal/frame/mod.rs +++ /dev/null @@ -1,312 +0,0 @@ -pub mod fragment; - -#[allow(dead_code)] -pub mod reliability; - -use std::io::{Cursor, Write}; - -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; - -use self::fragment::FragmentMeta; -use self::reliability::Reliability; - -/// Frames are a encapsulation of a packet or packets. -/// They are used to send packets to the connection in a reliable way. -#[derive(Debug, Clone)] -pub struct FramePacket { - /// The sequence of this frame. - /// We'll use this to respond with Ack and Nack to. - /// This is sized check to 24 bits. - pub sequence: u32, - - /// The frames for this frame packet, not to exceed the mtu size. - pub frames: Vec, - - /// This is internal use only. - pub(crate) reliability: Reliability, - - /// This is internal use only. - pub(crate) byte_length: usize, -} - -impl FramePacket { - /// Creates an empty frame packet. - pub fn new() -> Self { - Self { - sequence: 0, - frames: Vec::new(), - reliability: Reliability::ReliableOrd, - byte_length: 0, - } - } - - /// Paritions a stream into a bunch of fragments and returns a frame packet - /// that is partitioned, otherwise known as "fragmented". - /// This does not modify reliability. That is up to the caller. - pub fn partition(stream: Vec, id: u16, frag_size: u32) -> Vec { - let mut meta: FragmentMeta = FragmentMeta { - size: 0, - id, - index: 0, - }; - - let mut frames: Vec = Vec::new(); - let mut position: usize = 0; - - while position < stream.len() { - // check whether or not we can read the rest of of the stream - if stream[position..].len() < frag_size as usize { - // we can reliably read the rest of the buffer into a single frame. - let mut frame = Frame::init(); - frame.body = stream[position..].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - break; - } else { - // we can't read the rest of the stream into a single frame - // continue to split into multiple frames. - let mut frame = Frame::init(); - let to_pos = position + (frag_size as usize); - frame.body = stream[position..to_pos].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - position = to_pos; - meta.index += 1; - } - } - - meta.size = frames.len() as u32; - - // Let's fix up the meta data in each frame. - for frame in frames.iter_mut() { - if let Some(m) = frame.fragment_meta.as_mut() { - m.size = meta.size; - } - } - - return frames; - } -} - -impl Streamable for FramePacket { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source); - stream.set_position(*position as u64); - stream.read_u8()?; - let mut frames: Vec = Vec::new(); - let sequence = stream.read_u24::()?; - let mut offset: usize = stream.position() as usize; - - loop { - if stream.position() > source.len() as u64 { - return Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - byte_length: 0, - }); - } - - if stream.position() == source.len() as u64 { - break Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - byte_length: 0, - }); - } - - if let Ok(frame) = Frame::compose(&source, &mut offset) { - stream.set_position(offset as u64); - frames.push(frame.clone()); - - // if frame.parse()?.len() + stream.position() as usize > source.len() { - // return Ok(FramePacket { sequence, frames, byte_length: 0 }); - // } else { - // continue; - // } - } else { - return Err(BinaryError::RecoverableKnown( - "Frame composition failed! Failed to read frame.".into(), - )); - } - } - } - - fn parse(&self) -> Result, BinaryError> { - let mut stream = Cursor::new(Vec::new()); - stream.write_u8(0x80)?; - stream.write_u24::(self.sequence)?; - - for frame in &self.frames { - stream.write_all(&frame.parse()?)?; - } - - Ok(stream.into_inner()) - } -} - -/// An individual data frame, these are constructed from a payload. -#[derive(Debug, Clone)] -pub struct Frame { - /// The flags for this frame, the first 3 bits are reserved for the reliability while the 4th - /// bit is used to represent if this is a fragment. - pub flags: u8, - /// The length of the body of the frame. - /// This is sized to 24 bits internally, so any number here must be within that range. - pub size: u16, - /// The Reliable index of the frame (if reliable) - pub reliable_index: Option, - /// The sequenced index of the frame (if sequenced) - /// This is used to determine the position in frame list. - pub sequence_index: Option, - /// The order index of the frame (if ordered) - /// This is used to determine the position in frame list, - /// This is different from the sequence index in that it is - /// used more to sequence packets in a specific manner. - pub order_index: Option, - /// The order channel of the frame (if ordered) - /// This is used to store order information for the frame. - pub order_channel: Option, - /// The information for fragmentation (if the frame is split into parts) - /// This is used to determine how to reassemble the frame. - pub fragment_meta: Option, - /// The reliability of this frame, this is essentially used to save frames and send them back if - /// they are lost. Otherwise, the frame is sent unreliably. - pub reliability: Reliability, - /// The body of the frame, this is the payload of the frame. - pub body: Vec, -} - -impl Frame { - /// Initializes a new empty frame that is Unreliable. - /// This is usually used to inject data into. - pub fn init() -> Self { - Self { - flags: 0, - size: 0, - reliable_index: None, - sequence_index: None, - order_index: None, - order_channel: None, - fragment_meta: None, - reliability: Reliability::Unreliable, - body: Vec::new(), - } - } - - /// Whether or not the frame is fragmented. - pub fn is_fragmented(&self) -> bool { - self.fragment_meta.is_some() - } - - /// Whether or not the frame is sequenced and reliable. - pub fn is_sequenced(&self) -> bool { - self.reliability.is_sequenced() - } -} - -impl Streamable for Frame { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source.to_vec()); - - // create a dummy frame for us to write to. - let mut frame: Frame = Frame::init(); - - // set the position to the current position - stream.set_position(*position as u64); - - // read the flags - frame.flags = stream.read_u8()?; - // set the reliability - frame.reliability = Reliability::from_flags(frame.flags); - - // read the length of the body in bits - frame.size = stream.read_u16::()? / 8; - - // check whether or not this frame is reliable, if it is, read the reliable index - if frame.reliability.is_reliable() { - frame.reliable_index = Some(stream.read_u24::()?); - } - - // check whether or not this frame is sequenced, if it is, read the sequenced index - if frame.reliability.is_sequenced() { - frame.sequence_index = Some(stream.read_u24::()?); - } - - // check whether or not this frame is ordered, if it is, read the order index - // and order channel - if frame.reliability.is_sequenced_or_ordered() { - frame.order_index = Some(stream.read_u24::()?); - frame.order_channel = Some(stream.read_u8()?); - } - - // check whether or not this frame is fragmented, if it is, read the fragment meta - if (frame.flags & 0x10) > 0 { - frame.fragment_meta = Some(FragmentMeta { - size: stream.read_u32::()?.try_into().unwrap(), - id: stream.read_u16::()?, - index: stream.read_u32::()?.try_into().unwrap(), - }); - } - - // read the body - frame.body = (&source - [stream.position() as usize..stream.position() as usize + frame.size as usize]) - .to_vec(); - // update the position. - *position = stream.position() as usize + frame.size as usize; - - Ok(frame) - } - - fn parse(&self) -> Result, error::BinaryError> { - let mut stream = Cursor::new(Vec::new()); - // generate the flags! - let mut flags = self.reliability.to_flags(); - - // check whether or not this frame is fragmented, if it is, set the fragment flag - if self.fragment_meta.is_some() { - flags |= 0x10; - } - - let size = self.body.len() as u16; - - // write the flags - stream.write_u8(flags)?; - // write the length of the body in bits - stream.write_u16::(size * 8)?; - - // check whether or not this frame is reliable, if it is, write the reliable index - if self.reliability.is_reliable() { - stream.write_u24::(self.reliable_index.unwrap())?; - } - - // check whether or not this frame is sequenced, if it is, write the sequenced index - if self.reliability.is_sequenced() { - stream.write_u24::(self.sequence_index.unwrap())?; - } - - // check whether or not this frame is ordered, if it is, write the order index - // and order channel - if self.reliability.is_sequenced_or_ordered() { - stream.write_u24::(self.order_index.unwrap())?; - stream.write_u8(self.order_channel.unwrap())?; - } - - // check whether or not this frame is fragmented, if it is, write the fragment meta - if self.fragment_meta.is_some() { - let fragment_meta = self.fragment_meta.as_ref().unwrap(); - stream.write_u32::(fragment_meta.size.try_into().unwrap())?; - stream.write_u16::(fragment_meta.id)?; - stream.write_u32::(fragment_meta.index.try_into().unwrap())?; - } - - // write the body - stream.write_all(&self.body)?; - - Ok(stream.get_ref().clone()) - } -} diff --git a/src/internal/frame/reliability/cache.rs b/src/internal/frame/reliability/cache.rs deleted file mode 100644 index 65691b1..0000000 --- a/src/internal/frame/reliability/cache.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{collections::HashMap, time::SystemTime}; - -// this is a cache store for packets, -// any packets in here will be ticked, and over time, will be resent, or discarded -#[derive(Debug, Clone)] -pub struct CacheStore { - pub(crate) store: HashMap)>, -} - -impl CacheStore -where - K: std::hash::Hash + std::cmp::Eq, - V: ?Sized + Clone, -{ - pub fn new() -> Self { - Self { - store: HashMap::new(), - } - } - - pub fn add(&mut self, sequence: K, buffer: V) { - let ent = self - .store - .entry(sequence) - .or_insert((SystemTime::now(), Vec::new())); - ent.1.push(buffer); - } - - pub fn add_bulk(&mut self, sequence: K, buffers: Vec) { - let ent = self - .store - .entry(sequence) - .or_insert((SystemTime::now(), Vec::new())); - ent.1.extend(buffers); - } - - // clear old packets from the cache and return them - pub fn flush(&mut self) -> Vec<(K, SystemTime, Vec)> { - let mut flushed = Vec::new(); - for (sequence, (time, frames)) in self.store.drain() { - flushed.push((sequence, time, frames)); - } - return flushed; - } - - pub fn flush_key(&mut self, key: K) -> Option<(SystemTime, Vec)> { - self.store.remove(&key) - } - - pub fn has(&self, key: &K) -> bool { - self.store.contains_key(key) - } -} diff --git a/src/internal/frame/reliability/mod.rs b/src/internal/frame/reliability/mod.rs deleted file mode 100644 index a821eaf..0000000 --- a/src/internal/frame/reliability/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -pub mod cache; - -#[derive(Clone, Debug, Copy)] -#[repr(u8)] -pub enum Reliability { - /// Unreliable (with no ack) - Unreliable = 0, - /// Unreliable with a sequence - UnreliableSeq, - /// Reliable - Reliable, - ReliableOrd, - /// Reliably sequenced **AND** ordered - ReliableSeq, - UnreliableAck, - ReliableAck, - ReliableOrdAck, -} - -impl Reliability { - pub fn from_flags(flags: u8) -> Self { - match (flags & 224) >> 5 { - 0 => Reliability::Unreliable, - 1 => Reliability::UnreliableSeq, - 2 => Reliability::Reliable, - 3 => Reliability::ReliableOrd, - 4 => Reliability::ReliableSeq, - 5 => Reliability::UnreliableAck, - 6 => Reliability::ReliableAck, - 7 => Reliability::ReliableOrdAck, - // we shouldn't error, but we'll just return unreliable - _ => Reliability::Unreliable, - } - } - - pub fn to_flags(&self) -> u8 { - match self { - Reliability::Unreliable => 0 << 5, - Reliability::UnreliableSeq => 1 << 5, - Reliability::Reliable => 2 << 5, - Reliability::ReliableOrd => 3 << 5, - Reliability::ReliableSeq => 4 << 5, - Reliability::UnreliableAck => 5 << 5, - Reliability::ReliableAck => 6 << 5, - Reliability::ReliableOrdAck => 7 << 5, - } - } - - /// Whether or not the packet is ordered. - pub fn is_ordered(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => true, - _ => false, - } - } - - /// Whether or not the packet is reliable. - pub fn is_reliable(&self) -> bool { - match self { - Self::Reliable | Self::ReliableOrd | Self::ReliableSeq | Self::ReliableOrdAck => true, - _ => false, - } - } - - /// Whether or not the packet is unreliable. - pub fn is_unreliable(&self) -> bool { - match self { - Self::Unreliable | Self::UnreliableSeq | Self::UnreliableAck => true, - _ => false, - } - } - - /// Whether or not the packet is sequenced. - pub fn is_sequenced(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableSeq => true, - _ => false, - } - } - - pub fn is_sequenced_or_ordered(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => { - true - } - _ => false, - } - } - - /// Whether or not the packet has an acknowledgement. - pub fn is_ack(&self) -> bool { - match self { - Self::UnreliableAck | Self::ReliableAck | Self::ReliableOrdAck => true, - _ => false, - } - } -} diff --git a/src/internal/handler.rs b/src/internal/handler.rs deleted file mode 100644 index 44dce09..0000000 --- a/src/internal/handler.rs +++ /dev/null @@ -1,549 +0,0 @@ -use binary_utils::*; -use std::{ - collections::{HashMap, HashSet}, - fmt, - io::Write, -}; - -use crate::connection::Connection; - -use super::{ - ack::{Ack, Record}, - frame::{ - reliability::{cache::CacheStore, Reliability}, - Frame, FramePacket, - }, - queue::OrderedQueue, -}; - -#[cfg(feature = "debug")] -use crate::rak_debug; - -#[derive(Debug)] -pub enum RakHandlerError { - Unknown(String), - BinaryError(binary_utils::error::BinaryError), - UnknownPacket(u8), -} - -impl fmt::Display for RakHandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - RakHandlerError::Unknown(s) => write!(f, "Unknown error: {}", s), - RakHandlerError::BinaryError(e) => write!(f, "Binary error: {:?}", e), - RakHandlerError::UnknownPacket(p) => write!(f, "Unknown packet: {}", p), - } - } -} - -impl From for RakHandlerError { - fn from(s: String) -> Self { - RakHandlerError::Unknown(s) - } -} - -impl From for RakHandlerError { - fn from(e: binary_utils::error::BinaryError) -> Self { - RakHandlerError::BinaryError(e) - } -} - -/// The handler for Ack, Nack and Frame packets. -/// This does not handle the actual sending of packets, -#[derive(Debug, Clone)] -pub struct RakConnHandlerMeta { - /// The next Non-Acked packets that should be sent. - /// These are packets we expect back from the client, but have not gotten. - pub nack: HashSet, - /// The Acked packets that have been sent, waiting for ack back. (only if reliable) - /// This is used to determine if the packet has been received. - /// We're also storing the count of how many times we've sent a packet. - /// If it's been sent more than once, we'll consider it lost and remove it. - pub ack: CacheStore>, - /// A queue to send back to the client to acknowledge we've recieved these packets. - pub ack_counts: HashSet, - /// The ordered channels that have been recieved and are waiting for completion. - /// Ordered channels will be reorded once all the packets have been received. - pub ordered_channels: OrderedQueue>, - /// The fragmented frames that are waiting for reassembly. - pub fragmented_frames: HashMap>, - /// The sequence number used to send packets. - /// This is incremented every time we send a packet that is reliable. - /// Any packets that are reliable, can be re-sent if they are acked. - pub send_seq: u32, - /// The next order index to use for ordered channels. - /// This is incremented every time we send a packet with an order channel. - pub order_index: HashMap, - /// The next sequence index to use for ordered channels & sequenced packets. - /// Each sequence varies on the order channel. - /// This is incremented every time we send a packet with an order channel. - pub seq_index: HashMap, - /// The next message index, this is basically each reliable message. - /// This is incremented every time we send a packet with a reliable channel. - pub message_index: HashMap, - /// The fragment id to be used next. - pub fragment_ids: HashSet, -} - -impl RakConnHandlerMeta { - pub fn new() -> Self { - Self { - nack: HashSet::new(), - ack: CacheStore::new(), - ack_counts: HashSet::new(), - ordered_channels: OrderedQueue::new(), - fragmented_frames: HashMap::new(), - send_seq: 0, - order_index: HashMap::new(), - message_index: HashMap::new(), - seq_index: HashMap::new(), - fragment_ids: HashSet::new(), - } - } - - pub fn next_seq(&mut self) -> u32 { - self.send_seq += 1; - self.send_seq - } - - pub fn get_order_index(&mut self, channel: u8) -> u32 { - *self.order_index.entry(channel).or_insert(0) - } - - pub fn next_order_index(&mut self, channel: u8) -> u32 { - let index = self.order_index.entry(channel).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - #[allow(dead_code)] - pub fn get_reliable_index(&mut self, channel: u8) -> u32 { - *self.message_index.entry(channel.into()).or_insert(0) - } - - pub fn next_reliable_index(&mut self, channel: u8) -> u32 { - let index = self.message_index.entry(channel.into()).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - #[allow(dead_code)] - pub fn get_sequence_index(&mut self, channel: u8) -> u32 { - *self.seq_index.entry(channel).or_insert(0) - } - - pub fn next_sequence_index(&mut self, channel: u8) -> u32 { - let index = self.seq_index.entry(channel).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - pub fn next_fragment_id(&mut self) -> u16 { - let next = self.fragment_ids.len() as u16; - self.fragment_ids.insert(next); - next - } - - pub fn free_fragment_id(&mut self, id: u16) { - self.fragment_ids.remove(&id); - } -} - -/// This is hacked struct to allow mutability across the handler. -/// Not sure if this is the best way to do this but it will stay for now. -#[derive(Debug, Clone)] -pub struct RakConnHandler; - -impl RakConnHandler { - /// Handles the raw payload from the connection (without the header). - /// This will check the header and then handle the packet according to that header. - pub fn handle(connection: &mut Connection, payload: &[u8]) -> Result<(), RakHandlerError> { - // first get the id of the packet. - let maybe_id = payload.get(0); - - if !maybe_id.is_some() { - return Ok(()); - } - - let id = maybe_id.unwrap(); - - match id { - 0x80..=0x8d => { - // this is a frame packet - return Self::handle_raw_frame(connection, payload); - } - 0xa0 => { - // this is an NACK packet, we need to send this packet back! - // let's check to see if we even have this packet. - let nack = Ack::compose(payload, &mut 0)?; - - // check the records - for record in nack.records { - match record { - Record::Single(rec) => { - // we're looking for a single record. - if connection.rakhandler.ack.has(&rec.sequence) { - // flush the cache for only this sequence - if let Some(packets) = - connection.rakhandler.ack.flush_key(rec.sequence) - { - for packet in packets.1 { - connection.send(packet, true); - } - connection.rakhandler.ack_counts.remove(&rec.sequence); - } - } - // We don't have this record, but there's nothing we can do about it. - } - Record::Range(mut rec) => { - rec.fix(); - // we're looking for a range of records. - // we need to check if we have any of the records in the range. - // we'll check the ack map for each record in the range. - for i in rec.start..rec.end { - if connection.rakhandler.ack.has(&i) { - // flush the cache for only this sequence - if let Some(packets) = connection.rakhandler.ack.flush_key(i) { - for packet in packets.1 { - connection.send(packet, true); - } - connection.rakhandler.ack_counts.remove(&i); - } - } - } - } - } - } - - return Ok(()); - } - 0xc0 => { - // this is an ACK packet from the client, we can remove the packet from the ACK list (for real). - let ack = Ack::compose(payload, &mut 0)?; - - for record in ack.records { - match record { - Record::Single(rec) => { - // we're looking for a single record. - connection.rakhandler.nack.remove(&rec.sequence); - // connection.rakhandler.ack_counts.remove(&rec.sequence); - } - Record::Range(mut rec) => { - rec.fix(); - // we're looking for a range of records. - // we need to check if we have any of the records in the range. - // we'll check the ack map for each record in the range. - for i in rec.start..rec.end { - connection.rakhandler.nack.remove(&i); - // connection.rakhandler.ack_counts.remove(&i); - } - } - } - } - - return Ok(()); - } - _ => { - // this is an unknown packet, we don't know what to do with it. - return Err(RakHandlerError::UnknownPacket(*id)); - } - } - } - - /// Handles a raw frame packet. - /// This packet has not yet been validated nor constructed, - /// this method will parse and validate the packet as well as performing - /// necessary reliability actions. - fn handle_raw_frame( - connection: &mut Connection, - payload: &[u8], - ) -> Result<(), RakHandlerError> { - let frame_packet = FramePacket::compose(&payload, &mut 0)?; - - // let's handle each individual frame of the packet - for frame in frame_packet.frames { - if frame.reliability.is_reliable() { - connection - .rakhandler - .ack_counts - .insert(frame_packet.sequence); - } - if frame.is_fragmented() { - // The fragmented frame meta data. - let meta = frame.fragment_meta.as_ref().unwrap(); - // The fragmented frames bounded by this id. - let parts = connection - .rakhandler - .fragmented_frames - .entry(meta.id) - .or_insert(HashMap::new()); - - // We need to check if we have all the parts of the frame. - // If we do, we'll reassemble the frame. - if parts.len() != meta.size as usize { - // We don't have all the parts, lets add this part to the list. - parts.insert(meta.index, frame.clone()); - } - - if parts.len() == meta.size as usize { - // We have all the fragments, we can reassemble the frame. - // Sense we need to order this by their index, we need to sort the parts. - let mut parts = parts.iter().collect::>(); - parts.sort_by_key(|f| f.0); - - // our parts are now sorted, we can now reassemble the frame. - let mut buffer = Vec::new(); - for (_, frm) in parts { - buffer.write_all(&frm.body).unwrap(); - } - - // This is now an online packet! we can handle it. - // make a fake frame now. - let mut fake_frame = frame.clone(); - fake_frame.body = buffer; - fake_frame.fragment_meta = None; - - Self::handle_frame(connection, fake_frame.clone())?; - } - } else { - Self::handle_frame(connection, frame.clone())?; - } - } - - Ok(()) - } - - /// Handles a single frame within a packet. - /// This method really only handles the reliability of the packet, - /// in that, if it is ordered, it will order it as it was sent. - /// And other related utilities. - fn handle_frame(connection: &mut Connection, frame: Frame) -> Result<(), RakHandlerError> { - if frame.is_sequenced() || frame.reliability.is_reliable() { - if frame.reliability.is_ordered() { - // todo: Actually handle order - let id = frame.order_index.unwrap(); - let success = connection - .rakhandler - .ordered_channels - .insert(frame.body.clone(), id); - if success { - Self::handle_packet(connection, frame.body)?; - } else { - // this is an old or duplicated packet! - #[cfg(feature = "debug")] - rak_debug!("Duplicate packet! {:?}", frame); - } - } else { - // todo the frame is sequenced and reliable, we can handle it. - // todo remove this hack and actually handle the sequence! - Self::handle_packet(connection, frame.body)?; - } - } else { - Self::handle_packet(connection, frame.body)?; - } - - Ok(()) - } - - /// Sugar syntax method, does a few validations checks and sends the packet over to the - /// connection to be handled further. - fn handle_packet(connection: &mut Connection, packet: Vec) -> Result<(), RakHandlerError> { - // first try to handle the packet. - // let packet = Packet::compose(&packet, &mut 0)?; - // we should check ack here. - if packet.len() == 0 { - return Ok(()); - } - if packet[0] == 0xa0 || packet[0] == 0xc0 { - // this is an ack packet, we need to re-handle this. - Self::handle(connection, &packet)?; - } else { - connection.handle(packet); - } - Ok(()) - } - - /// This function will batch all the frames together and send them to the client with the specified - /// reliability. - /// - /// If the packet is unreliable, raknet will not perform any checks to ensure that the client - /// may request the packet again. - fn send_frames(connection: &mut Connection, mut frames: Vec, reliability: Reliability) { - // this will send each frame in it's own packet. if it's a fragmented. - if frames.len() == 0 { - return; - } - - // get the frames that are free now. - let mut sent: HashMap)> = HashMap::new(); - // these are the frames that can be freed from the sent list. - // this is used to renew fragments so we can have different parts - let mut free: Vec = Vec::new(); - - let mut order_index: Option = None; - let mut sequence: Option = None; - - // this is an initial check - if reliability.is_ordered() { - order_index = Some(connection.rakhandler.next_order_index(0)); - } else if reliability.is_sequenced() { - // we still need an order index, however we don't need to increase the index. - order_index = Some(connection.rakhandler.get_order_index(0)); - // increase the sequence for this channel. - sequence = Some(connection.rakhandler.next_sequence_index(0)); - } - - let mut outbound = FramePacket::new(); - outbound.reliability = reliability; - outbound.sequence = connection.rakhandler.next_seq(); - - // if we need to fragment, then we need to add some complexity, otherwise, we can just send the packet. - // i have no idea why the fuck this is like this, but it is, lol - for frame in frames.iter_mut() { - frame.reliability = reliability; - - if reliability.is_reliable() { - // this is a reliable frame! Let's write the sequence it's bound to. - frame.reliable_index = Some(connection.rakhandler.next_reliable_index(0)); - } - - if reliability.is_sequenced() { - // this is a sequenced frame! Let's write the sequence it's bound to. - frame.sequence_index = sequence; - } - - if reliability.is_sequenced_or_ordered() { - // this is an ordered frame! Let's write the order index it's bound to. - frame.order_channel = Some(0); - frame.order_index = Some(order_index.unwrap()); - } - - if frame.is_fragmented() { - if let Some(meta) = frame.fragment_meta.clone() { - // we need to free this fragment id if we've sent all the parts. - let parts = sent.entry(meta.id).or_insert((meta.size, Vec::new())); - parts.1.push(meta.index); - - if parts.1.len() == parts.0 as usize { - // we've sent all the parts, we can free this id. - sent.remove(&meta.id); - free.push(meta.id); - } - } - } - - if frame.fparse().len() + outbound.byte_length > (connection.mtu - 60).into() { - // we need to send this packet. - Self::send_frame(connection, &outbound); - outbound = FramePacket::new(); - outbound.reliability = reliability; - outbound.sequence = connection.rakhandler.next_seq(); - } else { - outbound.frames.push(frame.clone()); - } - } - - // send the last packet. - Self::send_frame(connection, &outbound); - - for id in free { - connection.rakhandler.free_fragment_id(id); - } - } - - /// This function will send the given frame packet to the client. - fn send_frame(connection: &mut Connection, frame: &FramePacket) { - if frame.reliability.is_reliable() { - // we need to add this to the reliable list. - // this is buffered and will die if the client doesn't respond. - let parsed = frame.fparse(); - connection - .rakhandler - .ack - .add(frame.sequence, parsed.clone()); - connection.send_immediate(parsed); - } else { - connection.send_immediate(frame.fparse()); - } - } - - /// This is an instant send, this will send the packet to the client immediately. - pub fn send_framed(connection: &mut Connection, payload: Vec, reliability: Reliability) { - if payload.len() < 60 || (payload.len() - 60) < connection.mtu.into() { - let mut frame = Frame::init(); - frame.body = payload; - Self::send_frames(connection, vec![frame], reliability); - } else { - let frames = FramePacket::partition( - payload, - connection.rakhandler.next_fragment_id(), - (connection.mtu - 60).into(), - ); - Self::send_frames(connection, frames, reliability); - } - } - - pub fn tick(connection: &mut Connection) { - // lets send the packets in the queue now. - let packets = connection.queue.flush(); - let mut current_frame_id: u16 = 0; - - for packet in packets { - // we need to handle these packets! - let mut frames = - FramePacket::partition(packet, current_frame_id, (connection.mtu - 60).into()); - for frame in frames.iter_mut() { - if frame.is_fragmented() { - if let Some(meta) = frame.fragment_meta.as_mut() { - meta.id = current_frame_id; - } - } - } - current_frame_id += 1; - Self::send_frames(connection, frames, Reliability::ReliableOrd); - } - - if connection.state.is_connected() { - // send the acks to the client that we got some packets - // // get missing packets and request them. - let missing = connection.rakhandler.ordered_channels.flush_missing(); - - if missing.len() != 0 { - let nack = Ack::from_missing(missing); - - #[cfg(feature = "debug")] - rak_debug!("NACK: {:#?}", nack); - - connection.send(nack.fparse(), true); - } - - // clear up the packets we've recieved. - let mut ack = Ack::new(connection.rakhandler.ack_counts.len() as u16, false); - for id in connection.rakhandler.ack_counts.iter() { - ack.push_record(*id); - } - - if ack.records.len() != 0 { - connection.rakhandler.ack_counts.clear(); - connection.send(ack.fparse(), true); - } - - // clean up the packets that we need to have an ack for. - let mut needs_cleared = Vec::::new(); - for (id, queue) in connection.rakhandler.ack.store.iter() { - if queue.0.elapsed().unwrap().as_secs() > 5 { - needs_cleared.push(*id); - } - } - for id in needs_cleared { - let packets = connection.rakhandler.ack.flush_key(id).unwrap().1; - for packet in packets { - connection.send_immediate(packet); - } - } - } - } -} diff --git a/src/internal/mod.rs b/src/internal/mod.rs deleted file mode 100644 index bacad12..0000000 --- a/src/internal/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -/// ACK related. -pub mod ack; -/// Frame related. -pub mod frame; - -/// A internal handler for connections -pub mod handler; - -/// Queues -#[allow(dead_code)] -pub mod queue; - -/// Internal utilities. -pub mod util; - -pub use self::handler::*; - -#[macro_export] -macro_rules! rak_debug { - ($($arg:tt)*) => { - if cfg!(feature = "dbg") { - println!($($arg)*); - } - }; -} diff --git a/src/internal/util.rs b/src/internal/util.rs deleted file mode 100644 index fc2a018..0000000 --- a/src/internal/util.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::net::{SocketAddr, ToSocketAddrs}; - -pub fn to_address_token(remote: SocketAddr) -> String { - let mut address = remote.ip().to_string(); - address.push_str(":"); - address.push_str(remote.port().to_string().as_str()); - return address; -} - -pub fn from_address_token(remote: String) -> SocketAddr { - let mut parsed = remote - .to_socket_addrs() - .expect("Could not parse remote address."); - SocketAddr::from(parsed.next().unwrap()) -} diff --git a/src/lib.rs b/src/lib.rs index 07e2f4b..d42ef61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,2 @@ -#![feature(cursor_remaining)] -extern crate binary_utils; - -/// A unique identifier recoginzing the client as offline. -pub const MAGIC: [u8; 16] = [ - 0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, -]; - -/// Internal utilities for raknet -/// These are used in rakrs to parse packets and are not exposed to the user. -pub(crate) mod internal; - -/// Home of the RakNet protocol. -/// This contains some generic handling for the protocol. -/// If you're looking for mcpe specific handling you need -/// to enable the `mcpe` feature. -pub mod protocol; - -/// Raknet sessions. -/// These should be used to communicate with other players. (on the server) -/// A session is a "connection" to a player. It serves as the interface for -/// communicating with a client. -pub mod connection; - -/// The raknet server -/// This is the main entry point for the server. pub mod server; - -// Export the entire server module for ease of use -pub use self::server::*; +pub mod conn; \ No newline at end of file diff --git a/src/protocol/mcpe/mod.rs b/src/protocol/mcpe/mod.rs deleted file mode 100644 index 2de0284..0000000 --- a/src/protocol/mcpe/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Minecrafts specific protocol for the `UnconnectedPong` packet. -/// This data is attached to the Unconnect Pong packet and is used to -/// display information about the server. -/// -/// EG: -/// ```markdown -/// Netrex Server v1.18.0 - 1/1 players online -/// ``` -pub mod motd; - -/// Packet specific data for Minecraft, for now it's just the `UnconnectedPong` packet. -pub mod packet; diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs deleted file mode 100644 index 36d0f4a..0000000 --- a/src/protocol/mcpe/motd.rs +++ /dev/null @@ -1,204 +0,0 @@ -use binary_utils::Streamable; - -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Gamemode { - Survival = 0, - Creative, - Adventure, - Spectator, -} - -impl Gamemode { - pub fn as_str(&self) -> &'static str { - match self { - Gamemode::Survival => "Survival", - Gamemode::Creative => "Creative", - Gamemode::Adventure => "Adventure", - Gamemode::Spectator => "Spectator", - } - } -} - -impl std::fmt::Display for Gamemode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let v = match *self { - Gamemode::Survival => "0", - Gamemode::Creative => "1", - Gamemode::Adventure => "2", - Gamemode::Spectator => "3", - }; - write!(f, "{}", v) - } -} - -/// Protocol wise, motd is just a string -/// However we're using this struct to represent the motd -#[derive(Debug, Clone)] -pub struct Motd { - /// The name of the server - pub name: String, - /// The protocol version - pub protocol: u16, - /// The version of the server - pub version: String, - /// The number of players online - pub player_count: u16, - /// The maximum number of players - pub player_max: u16, - /// The gamemode of the server - pub gamemode: Gamemode, - /// The server's GUID - pub server_guid: u64, - /// The server's port - pub port: String, - /// The IPv6 port - /// TODO: Implement this - pub ipv6_port: String, -} - -impl Motd { - pub fn new>(server_guid: u64, port: S) -> Self { - Self { - name: "Netrex Server".into(), - player_count: 10, - player_max: 100, - protocol: 448, - gamemode: Gamemode::Survival, - version: "1.18.0".into(), - server_guid, - port: port.into(), - ipv6_port: "19133".into(), - } - } - - /// Takes the Motd and parses it into a valid MCPE - /// MOTD buffer. - pub fn write(&self) -> String { - let props: Vec = vec![ - "MCPE".into(), - self.name.clone(), - self.protocol.to_string(), - self.version.clone(), - self.player_count.to_string(), - self.player_max.to_string(), - self.server_guid.to_string(), - "Netrex".to_string(), - self.gamemode.as_str().to_string(), - "1".to_string(), - // Todo: Figure out why this is not working - // self.gamemode.to_string(), - self.port.to_string(), - self.ipv6_port.to_string(), - ]; - props.join(";").into() - } -} - -impl Streamable for Motd { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let motd = String::compose(source, position)?; - let parts = motd - .split(";") - .map(|c| c.to_string()) - .collect::>(); - let name = parts - .get(1) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd name".into(), - ))?; - let protocol = parts - .get(2) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd protocol".into(), - ))?; - let version = parts - .get(3) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd version".into(), - ))?; - let player_count = - parts - .get(4) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player count".into(), - ))?; - let player_max = parts - .get(5) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player maxmium".into(), - ))?; - let server_guid = - parts - .get(6) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd server guid".into(), - ))?; - let _ = parts - .get(7) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd software name".into(), - ))?; - let _ = parts - .get(8) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode string".into(), - ))?; - let gamemode = parts - .get(9) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode".into(), - ))?; - let port = parts - .get(10) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; - let ipv6_port = parts - .get(11) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; - - Ok(Motd { - name: name.clone(), - protocol: protocol - .as_str() - .parse::() - .expect("Invalid motd protocol"), - version: version.clone(), - player_count: player_count - .as_str() - .parse::() - .expect("Player count is not a number"), - player_max: player_max - .as_str() - .parse::() - .expect("Player Maximum is not a number"), - server_guid: server_guid - .as_str() - .parse::() - .expect("Server GUID is not a number"), - port: port.clone(), - ipv6_port: ipv6_port.clone(), - gamemode: match gamemode - .as_str() - .parse::() - .expect("Gamemode is not a byte") - { - 0 => Gamemode::Survival, - 1 => Gamemode::Creative, - 2 => Gamemode::Adventure, - 3 => Gamemode::Spectator, - _ => Gamemode::Survival, - }, - }) - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - self.write().parse() - } -} diff --git a/src/protocol/mcpe/packet.rs b/src/protocol/mcpe/packet.rs deleted file mode 100644 index 2280162..0000000 --- a/src/protocol/mcpe/packet.rs +++ /dev/null @@ -1,15 +0,0 @@ -use binary_utils::*; - -use crate::protocol::PacketId; -use crate::{packet_id, protocol::util::Magic}; - -use super::motd::Motd; - -#[derive(Debug, Clone, BinaryStream)] -pub struct UnconnectedPong { - pub timestamp: u64, - pub server_id: u64, - pub magic: Magic, - pub motd: Motd, -} -packet_id!(UnconnectedPong, 0x1c); diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs deleted file mode 100644 index e597741..0000000 --- a/src/protocol/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod packet; -/// Packet Utilities -pub use packet::*; - -// #[cfg(feature = "mcpe")] -pub mod mcpe; - -/// Protocol utilities (structs) -pub mod util; diff --git a/src/protocol/packet/handler.rs b/src/protocol/packet/handler.rs deleted file mode 100644 index ff6d78a..0000000 --- a/src/protocol/packet/handler.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::time::SystemTime; - -use crate::connection::state::ConnectionState; -use crate::internal::queue::SendPriority; -use crate::internal::util::from_address_token; -use crate::protocol::util::Magic; -use crate::rak_debug; -use crate::{connection::Connection, server::RakEvent}; - -use super::offline::{IncompatibleProtocolVersion, OpenConnectReply, SessionInfoReply}; -use super::online::{ConnectedPong, ConnectionAccept, OnlinePacket}; -use super::OfflinePacket; -use super::{offline::UnconnectedPong, Packet}; - -/// The offline packet handler, responsible for handling -/// Ping, Pong, and other packets. -pub fn handle_offline(connection: &mut Connection, packet: Packet) { - // check if the type of packet, we'll use a match statement - let result = match packet.get_offline() { - OfflinePacket::UnconnectedPing(_) => { - // if the packet is a ping, we'll send a pong - // and dispatch an event to update the Motd. - connection.event_dispatch.push_back(RakEvent::Motd( - connection.address.clone(), - connection.motd.clone(), - )); - - // send the pong to the server, and parse it! - // we could compensate for decoding time, but there isn't - // too much overhead there, so we'll just send as is. - let pong = UnconnectedPong { - server_id: connection.server_guid, - timestamp: connection.start_time.elapsed().unwrap().as_millis() as u64, - magic: Magic::new(), - #[cfg(feature = "mcpe")] - motd: connection.motd.clone(), - }; - connection.send_packet(pong.into(), SendPriority::Immediate); - Ok(()) - } - OfflinePacket::OpenConnectRequest(pk) => { - if pk.protocol != connection.raknet_version.to_u8() { - let incompatible = IncompatibleProtocolVersion { - protocol: pk.protocol, - magic: Magic::new(), - server_id: connection.server_guid, - }; - connection.send_packet(incompatible.into(), SendPriority::Immediate); - } - - // The version is valid, we can send the reply. - let reply = OpenConnectReply { - server_id: connection.server_guid, - // todo: Make this optional - security: false, - magic: Magic::new(), - mtu_size: pk.mtu_size, - }; - - // we can actually save the requested mtu size from the client - connection.mtu = pk.mtu_size; - connection.send_packet(reply.into(), SendPriority::Immediate); - Ok(()) - } - OfflinePacket::SessionInfoRequest(pk) => { - // todo: Actually check if we want the client to join the server! - // todo: And disconnect them if we don't! - let reply = SessionInfoReply { - server_id: connection.server_guid, - client_address: from_address_token(connection.address.clone()), - magic: Magic::new(), - mtu_size: pk.mtu_size, - // todo: Again, make this optional - security: false, - }; - // the client is now officially in the "Connecting State" - // let's validate the mtu - if pk.mtu_size != connection.mtu { - connection.mtu = pk.mtu_size; - #[cfg(feature = "dbg")] - rak_debug!( - "[RakNet] [{}] Recieved two different MTU sizes, setting to {}", - connection.address, - connection.mtu - ); - } - - // the client is actually trying to connect. - connection.state = ConnectionState::Connecting; - connection.send_packet(reply.into(), SendPriority::Immediate); - Ok(()) - } - _ => { - Err("A client can not send this packet, or the packet is not implemented for offline!") - } - }; - - if let Err(e) = result { - // we're not going to panic because that would be bad in prod, so we'll just log it. - rak_debug!( - "[RakNet] [{}] Received an offline packet that is not! {:?}", - connection.address, - e - ); - }; -} - -pub fn handle_online(connection: &mut Connection, packet: Packet) -> Result<(), &str> { - match packet.get_online() { - OnlinePacket::ConnectedPing(pk) => { - let response = ConnectedPong { - ping_time: pk.time, - pong_time: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, - }; - connection.send_packet(response.into(), SendPriority::Immediate); - Ok(()) - } - OnlinePacket::ConnectionRequest(pk) => { - let response = ConnectionAccept { - system_index: 0, - client_address: from_address_token(connection.address.clone()), - internal_id: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19132), - request_time: pk.time, - timestamp: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, - }; - connection.send_packet(response.into(), SendPriority::Immediate); - Ok(()) - } - OnlinePacket::Disconnect(_) => { - // Disconnect the client immediately. - connection.disconnect("Client disconnected.", false); - Ok(()) - } - OnlinePacket::NewConnection(_) => { - connection.state = ConnectionState::Connected; - Ok(()) - } - _ => Err("A client can not send this packet, or the packet is not implemented for online!"), - } -} diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs deleted file mode 100644 index 34ad3a0..0000000 --- a/src/protocol/packet/mod.rs +++ /dev/null @@ -1,352 +0,0 @@ -/// Handlers for both online & offline packets! -/// This is used by the connection struct to handle packets. -pub(crate) mod handler; - -/// The protocol that is used when a client is considered "Online". -/// Sessions in this state are usually: Connected or Connecting -pub mod online; - -/// The protocol that is used when a client is considered "Unidentified". -/// This module is used for Pong and connection requests. -pub mod offline; - -use std::io::Write; - -use binary_utils::Streamable; -use byteorder::WriteBytesExt; - -use self::offline::{ - IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, - SessionInfoRequest, UnconnectedPing, UnconnectedPong, -}; -use self::online::{ - ConnectedPing, ConnectedPong, ConnectionAccept, ConnectionRequest, Disconnect, LostConnection, - NewConnection, -}; - -use super::offline::OfflinePacket; -use super::online::OnlinePacket; - -/// A helper trait to identify packets. -pub trait PacketId { - fn id() -> u8; - - fn get_id(&self) -> u8 { - Self::id() - } -} - -/// A Generic Packet. -/// This is the base for all packets. -#[derive(Clone, Debug)] -pub struct Packet { - /// The packet id. - pub id: u8, - /// The packet data. (this is the payload) - pub payload: Payload, -} - -impl Packet { - pub fn is_online(&self) -> bool { - match self.payload { - Payload::Online(_) => true, - _ => false, - } - } - - pub fn is_offline(&self) -> bool { - return !self.is_online(); - } - - pub fn get_online(&self) -> OnlinePacket { - self.clone().into() - } - - pub fn get_offline(&self) -> OfflinePacket { - self.clone().into() - } -} - -impl Streamable for Packet { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let payload = Payload::compose(source, position)?; - let id = source[0]; - Ok(Packet { id, payload }) - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - buffer.write_u8(self.id)?; - buffer.write_all(&self.payload.parse()?)?; - Ok(buffer) - } -} - -#[derive(Clone, Debug)] -pub enum Payload { - Online(OnlinePacket), - Offline(OfflinePacket), -} - -impl Streamable for Payload { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - // we need the id! - let id = u8::compose(source, position)?; - - match id { - x if x == UnconnectedPing::id() => { - let packet = - OfflinePacket::UnconnectedPing(UnconnectedPing::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == UnconnectedPong::id() => { - let packet = - OfflinePacket::UnconnectedPong(UnconnectedPong::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectRequest::id() => { - let packet = OfflinePacket::OpenConnectRequest(OpenConnectRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectReply::id() => { - let packet = - OfflinePacket::OpenConnectReply(OpenConnectReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoRequest::id() => { - let packet = OfflinePacket::SessionInfoRequest(SessionInfoRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoReply::id() => { - let packet = - OfflinePacket::SessionInfoReply(SessionInfoReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == IncompatibleProtocolVersion::id() => { - let packet = OfflinePacket::IncompatibleProtocolVersion( - IncompatibleProtocolVersion::compose(source, position)?, - ); - Ok(Payload::Offline(packet)) - } - x if x == ConnectedPing::id() => { - let packet = OnlinePacket::ConnectedPing(ConnectedPing::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectedPong::id() => { - let packet = OnlinePacket::ConnectedPong(ConnectedPong::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == LostConnection::id() => { - let packet = - OnlinePacket::LostConnection(LostConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionRequest::id() => { - let packet = - OnlinePacket::ConnectionRequest(ConnectionRequest::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionAccept::id() => { - let packet = - OnlinePacket::ConnectionAccept(ConnectionAccept::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == NewConnection::id() => { - let packet = OnlinePacket::NewConnection(NewConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == Disconnect::id() => { - let packet = OnlinePacket::Disconnect(Disconnect::compose(source, position)?); - Ok(Payload::Online(packet)) - } - _ => Err(binary_utils::error::BinaryError::RecoverableKnown(format!( - "Id is not a valid raknet packet: {}", - id - ))), - } - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - // we're not concerned about the id here so we're going to only write the payload! - let payload = match self { - Payload::Online(packet) => match packet { - OnlinePacket::ConnectedPing(pk) => pk.parse()?, - OnlinePacket::ConnectedPong(pk) => pk.parse()?, - OnlinePacket::LostConnection(pk) => pk.parse()?, - OnlinePacket::ConnectionRequest(pk) => pk.parse()?, - OnlinePacket::ConnectionAccept(pk) => pk.parse()?, - OnlinePacket::NewConnection(pk) => pk.parse()?, - OnlinePacket::Disconnect(pk) => pk.parse()?, - }, - Payload::Offline(packet) => match packet { - OfflinePacket::UnconnectedPing(pk) => pk.parse()?, - OfflinePacket::UnconnectedPong(pk) => pk.parse()?, - OfflinePacket::OpenConnectRequest(pk) => pk.parse()?, - OfflinePacket::OpenConnectReply(pk) => pk.parse()?, - OfflinePacket::SessionInfoRequest(pk) => pk.parse()?, - OfflinePacket::SessionInfoReply(pk) => pk.parse()?, - OfflinePacket::IncompatibleProtocolVersion(pk) => pk.parse()?, - }, - }; - if let Err(_) = buffer.write_all(&payload) { - Err(binary_utils::error::BinaryError::RecoverableKnown( - "Failed to write payload to buffer".to_string(), - )) - } else { - Ok(buffer) - } - } -} - -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let online: OnlinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OnlinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Online(x) => x, - _ => panic!("This is not an online packet!"), - } - } -} - -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let offline: OfflinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OfflinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Offline(x) => x, - _ => panic!("This is not an online packet!"), - } - } -} - -/// This implementation allows for conversion of a OnlinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let payload: Payload = OnlinePacket(_).into(); -/// ``` -impl From for Payload { - fn from(packet: OnlinePacket) -> Self { - Payload::Online(packet) - } -} - -/// This implementation allows for conversion of a OfflinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let payload: Payload = OfflinePacket(_).into(); -/// ``` -impl From for Payload { - fn from(packet: OfflinePacket) -> Self { - Payload::Offline(packet) - } -} - -/// A utility macro to add the `PacketId` trait to a packet. -/// This allows easier decoding of that packet. -#[macro_export] -macro_rules! packet_id { - ($name: ident, $id: literal) => { - impl PacketId for $name { - fn id() -> u8 { - $id - } - } - }; -} - -/// A utility macro that adds the implementation for any `OnlinePacket(Pk)` where -/// `Pk` can be converted to `Packet`, `Payload`, `OnlinePacket` or `OfflinePacket` -/// and vice versa. -/// -/// For example, we want unconnected pong to be unwrapped, we can do this without -/// a match statement like this: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// use raknet::packet::online::UnconnectedPing; -/// let some_packet = Packet::compose(&source, &mut position)?; -/// let connected_ping: UnconnectedPing = some_packet.into(); -/// ``` -/// -/// This macro also allows for converting any `OnlinePacket(Pk)` to a `Packet`, where `Pk` can -/// be directly converted into a packet. For example: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// use raknet::packet::online::UnconnectedPong; -/// -/// let packet: Packet = UnconnectedPong { -/// magic: Magic::new(), -/// timestamp: SystemTime::now(), -/// client_id: -129 -/// }.into(); -/// ``` -#[macro_export] -macro_rules! register_packets { - ($name: ident is $kind: ident, $($packet: ident),*) => { - $( - impl From<$packet> for $kind { - fn from(packet: $packet) -> Self { - $kind::$packet(packet) - } - } - - impl From<$kind> for $packet { - fn from(packet: $kind) -> Self { - match packet { - $kind::$packet(packet) => packet, - _ => panic!("Invalid packet type") - } - } - } - - impl From<$packet> for Packet { - fn from(payload: $packet) -> Self { - Self { - id: payload.get_id(), - payload: Payload::$name(payload.into()), - } - } - } - - impl From<$packet> for Payload { - fn from(payload: $packet) -> Self { - Self::$name(payload.into()) - } - } - - impl From for $packet { - fn from(payload: Payload) -> Self { - match payload { - Payload::$name(v) => v.into(), - _ => panic!("Invalid payload type"), - } - } - } - )* - }; -} diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs deleted file mode 100644 index 5db0f94..0000000 --- a/src/protocol/packet/offline.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::io::Write; -use std::net::SocketAddr; - -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::WriteBytesExt; - -#[cfg(feature = "mcpe")] -pub use crate::protocol::mcpe::packet::UnconnectedPong; - -use super::Packet; -use super::PacketId; -use super::Payload; -use crate::protocol::util::Magic; -use crate::{packet_id, register_packets}; - -/// A enum that represents all offline packets. -#[derive(Clone, Debug)] -pub enum OfflinePacket { - UnconnectedPing(UnconnectedPing), - OpenConnectRequest(OpenConnectRequest), - OpenConnectReply(OpenConnectReply), - SessionInfoRequest(SessionInfoRequest), - SessionInfoReply(SessionInfoReply), - #[cfg(feature = "mcpe")] - UnconnectedPong(UnconnectedPong), - #[cfg(not(feature = "mcpe"))] - UnconnectedPong(UnconnectedPong), - IncompatibleProtocolVersion(IncompatibleProtocolVersion), -} - -register_packets![ - Offline is OfflinePacket, - UnconnectedPing, - UnconnectedPong, - OpenConnectRequest, - OpenConnectReply, - SessionInfoRequest, - SessionInfoReply, - IncompatibleProtocolVersion -]; - -/// Unconnected Ping -#[derive(Debug, Clone, BinaryStream)] -pub struct UnconnectedPing { - pub timestamp: u64, - pub magic: Magic, - pub client_id: i64, -} -packet_id!(UnconnectedPing, 0x01); - -/// Unconnected Pong -#[cfg(not(feature = "mcpe"))] -#[derive(Debug, Clone, BinaryStream)] -pub struct UnconnectedPong { - pub timestamp: u64, - pub server_id: u64, - pub magic: Magic, -} -#[cfg(not(feature = "mcpe"))] -packet_id!(UnconnectedPong, 0x1c); - -/// This packet is the equivelant of the `OpenConnectRequest` packet in RakNet. -#[derive(Debug, Clone)] -pub struct OpenConnectRequest { - pub magic: Magic, - pub protocol: u8, - pub mtu_size: u16, -} -impl Streamable for OpenConnectRequest { - fn compose(source: &[u8], position: &mut usize) -> Result { - Ok(Self { - magic: Magic::compose(source, position)?, - protocol: u8::compose(source, position)?, - mtu_size: (source.len() + 1 + 28) as u16, - }) - } - - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::::new(); - stream - .write(&self.magic.parse()?[..]) - .expect("Failed to parse open connect request"); - stream.write_u8(self.protocol)?; - // padding - for _ in 0..self.mtu_size { - stream.write_u8(0)?; - } - Ok(stream) - } -} -packet_id!(OpenConnectRequest, 0x05); - -// Open Connection Reply -/// Sent to the client when the server accepts a client. -/// This packet is the equivalent of the `Open Connect Reply 1` packet. -#[derive(Debug, Clone, BinaryStream)] -pub struct OpenConnectReply { - pub magic: Magic, - pub server_id: u64, - pub security: bool, - pub mtu_size: u16, -} -packet_id!(OpenConnectReply, 0x06); - -/// Session info, also known as Open Connect Request 2 -#[derive(Debug, Clone, BinaryStream)] -pub struct SessionInfoRequest { - pub magic: Magic, - pub address: SocketAddr, - pub mtu_size: u16, - pub client_id: i64, -} -packet_id!(SessionInfoRequest, 0x07); - -/// Session Info Reply, also known as Open Connect Reply 2 -#[derive(Debug, Clone, BinaryStream)] -pub struct SessionInfoReply { - pub magic: Magic, - pub server_id: u64, - pub client_address: SocketAddr, - pub mtu_size: u16, - pub security: bool, -} -packet_id!(SessionInfoReply, 0x08); - -#[derive(Debug, Clone, BinaryStream)] -pub struct IncompatibleProtocolVersion { - pub protocol: u8, - pub magic: Magic, - pub server_id: u64, -} -packet_id!(IncompatibleProtocolVersion, 0x19); diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs deleted file mode 100644 index d645932..0000000 --- a/src/protocol/packet/online.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::io::Write; -use std::net::IpAddr; -use std::net::Ipv4Addr; -use std::net::SocketAddr; - -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::BigEndian; -use byteorder::WriteBytesExt; - -use super::Packet; -use super::PacketId; -use super::Payload; -use crate::{packet_id, register_packets}; - -/// A enum that represents all online packets. -#[derive(Clone, Debug)] -pub enum OnlinePacket { - ConnectedPing(ConnectedPing), - ConnectedPong(ConnectedPong), - LostConnection(LostConnection), - ConnectionRequest(ConnectionRequest), - ConnectionAccept(ConnectionAccept), - NewConnection(NewConnection), - Disconnect(Disconnect), -} - -register_packets![ - Online is OnlinePacket, - ConnectedPing, - ConnectedPong, - ConnectionRequest, - ConnectionAccept, - NewConnection, - Disconnect -]; - -/// Connected Ping Packet -/// This packet is sent by the client to the server. -/// The server should respond with a `ConnectedPong` packet. -#[derive(Clone, Debug, BinaryStream)] -pub struct ConnectedPing { - pub time: i64, -} -packet_id!(ConnectedPing, 0x00); - -/// Connected Pong Packet -/// This packet is sent by the server to the client in response to a `ConnectedPing` packet. -#[derive(Clone, Debug, BinaryStream)] -pub struct ConnectedPong { - pub ping_time: i64, - pub pong_time: i64, -} -packet_id!(ConnectedPong, 0x03); - -/// A connection Request Request, this contains information about the client. Like it's -/// current time and the client id. -#[derive(Clone, Debug, BinaryStream)] -pub struct ConnectionRequest { - pub client_id: i64, - pub time: i64, -} -packet_id!(ConnectionRequest, 0x09); - -/// A connection Accept packet, this is sent by the server to the client. -/// This is sent by the server and contains information about the server. -#[derive(Clone, Debug)] -pub struct ConnectionAccept { - /// The address of the client connecting (locally?). - pub client_address: SocketAddr, - /// The system index is the index of the system that the client is connected to. - /// This is the index of the server on the client. - /// (Not sure why this is useful) - pub system_index: i16, - /// The internal id's of the server or alternative IP's of the server. - /// These are addresses the client will use if it can't connect to the server. - /// (Not sure why this is useful) - pub internal_id: SocketAddr, - /// The time of the timestamp the client sent with `ConnectionRequest`. - pub request_time: i64, - /// The time on the server. - pub timestamp: i64, -} - -impl Streamable for ConnectionAccept { - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::new(); - stream.write_all(&self.client_address.parse()?[..])?; - stream.write_i16::(self.system_index)?; - for _ in 0..10 { - stream.write_all(&self.internal_id.parse()?[..])?; - } - stream.write_i64::(self.request_time)?; - stream.write_i64::(self.timestamp)?; - Ok(stream) - } - - fn compose(_source: &[u8], _position: &mut usize) -> Result { - Ok(Self { - client_address: SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120), - system_index: 0, - internal_id: SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), 1920), - request_time: 0, - timestamp: 0, - }) - } -} -packet_id!(ConnectionAccept, 0x10); - -/// Going to be completely Honest here, I have no idea what this is used for right now, -/// even after reading the source code. -#[derive(Clone, Debug, BinaryStream)] -pub struct NewConnection { - /// The external IP Address of the server. - pub server_address: SocketAddr, - /// The internal IP Address of the server. - pub system_address: SocketAddr, - /// The time of the timestamp the client sent with `ConnectionRequest`. - pub request_time: i64, - /// The time on the server. - pub timestamp: i64, -} -packet_id!(NewConnection, 0x13); - -/// A disconnect notification. Tells the client to disconnect. -#[derive(Clone, Debug, BinaryStream)] -pub struct Disconnect {} -packet_id!(Disconnect, 0x15); - -/// A connection lost notification. -/// This is sent by the client when it loses connection to the server. -#[derive(Clone, Debug, BinaryStream)] -pub struct LostConnection {} -packet_id!(LostConnection, 0x04); diff --git a/src/protocol/util.rs b/src/protocol/util.rs deleted file mode 100644 index 3579680..0000000 --- a/src/protocol/util.rs +++ /dev/null @@ -1,33 +0,0 @@ -use binary_utils::{error::BinaryError, Streamable}; - -use crate::MAGIC; - -#[derive(Debug, Clone)] -pub struct Magic(pub Vec); - -impl Magic { - pub fn new() -> Self { - Self(MAGIC.to_vec()) - } -} - -impl Streamable for Magic { - fn parse(&self) -> Result, BinaryError> { - Ok(MAGIC.to_vec()) - } - - fn compose(source: &[u8], position: &mut usize) -> Result { - // magic is 16 bytes - let pos = *position + (16 as usize); - let magic = &source[*position..pos]; - *position += 16; - - if magic.to_vec() != MAGIC.to_vec() { - Err(BinaryError::RecoverableKnown( - "Could not construct magic from malformed bytes.".to_string(), - )) - } else { - Ok(Self(magic.to_vec())) - } - } -} diff --git a/src/server/mod.rs b/src/server/mod.rs index 40cd6c4..fb42687 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,11 +1,20 @@ -#[cfg(feature = "async_tokio")] -mod tokio; +use std::{sync::Arc, net::SocketAddr}; -#[cfg(feature = "async_tokio")] -pub use self::tokio::*; +use tokio::net::UdpSocket; -#[cfg(feature = "async_std")] -mod std; +pub struct Listener { + /// The current socket. + sock: Option>, + /// Whether or not to use minecraft specific protocol. + /// This only effects the `Pong` packet where motd is sent. + mcpe: bool, + /// If mcpe is true, this is the default MOTD, this is + /// the default MOTD to send to the client. + motd: String, +} -#[cfg(feature = "async_std")] -pub use self::std::*; +impl Listener { + pub async fn bind(address: SocketAddr) { + + } +} \ No newline at end of file diff --git a/src/server/motd.rs b/src/server/motd.rs new file mode 100644 index 0000000..1852e30 --- /dev/null +++ b/src/server/motd.rs @@ -0,0 +1,3 @@ +pub struct Motd { + +} \ No newline at end of file diff --git a/src/server/std.rs b/src/server/std.rs deleted file mode 100644 index 16aed2c..0000000 --- a/src/server/std.rs +++ /dev/null @@ -1 +0,0 @@ -// std code. diff --git a/src/server/tokio.rs b/src/server/tokio.rs deleted file mode 100644 index e52a313..0000000 --- a/src/server/tokio.rs +++ /dev/null @@ -1,357 +0,0 @@ -use futures::Future; -use netrex_events::Channel; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; -use std::sync::RwLock; -use std::time::Duration; -use std::time::SystemTime; -use tokio::net::UdpSocket; -use tokio::time::sleep; - -use crate::connection::state::ConnectionState; -use crate::connection::Connection; -use crate::internal::queue::SendPriority; -use crate::internal::util::from_address_token; -use crate::internal::util::to_address_token; -use crate::protocol::mcpe::motd::Motd; -use crate::rak_debug; - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -#[repr(u8)] -pub enum RakNetVersion { - V10 = 10, - V6 = 6, -} - -impl RakNetVersion { - pub fn to_u8(&self) -> u8 { - match self { - RakNetVersion::V10 => 10, - RakNetVersion::V6 => 6, - } - } -} - -#[derive(Clone, Debug)] -pub enum RakEvent { - /// When a connection is created - /// - /// ! This is not the same as connecting to the server ! - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - ConnectionCreated(String), - /// When a connection disconnects from the server - /// Or the server forces the connection to disconnect - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The reason for disconnect. - Disconnect(String, String), - /// When a connection is sent a motd. - /// You should return a Motd here if you want to change the MOTD. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The `Motd` that might be sent. - Motd(String, Motd), - /// When a game packet is recieved. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The packet `Vec` recieved from the connection. - GamePacket(String, Vec), - /// When RakNet Errors in some way that is recoverable. - /// - /// **Tuple Values**: - /// 1. The message to the error. - Error(String), - /// When a RakNet packet fails to parse or read a packet. - /// While the reason can be anything, this is considered a level 2 error (almost critical) - /// and should be handled by the server properly. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` of the connection that the packet was parsed for. - /// 2. The packet `Vec` that was supposed to succeed. - /// 3. The reason `String` for failing. - ComplexBinaryError(String, Vec, String), -} - -impl RakEvent { - pub fn get_name(&self) -> String { - match self { - RakEvent::ConnectionCreated(_) => "ConnectionCreated".into(), - RakEvent::Disconnect(_, _) => "Disconnect".into(), - RakEvent::GamePacket(_, _) => "GamePacket".into(), - RakEvent::Motd(_, _) => "Motd".into(), - RakEvent::Error(_) => "Error".into(), - RakEvent::ComplexBinaryError(_, _, _) => "ComplexBinaryError".into(), - } - } -} - -#[derive(Clone, Debug)] -pub enum RakResult { - /// Update the Motd for that specific client. - /// - /// **Tuple Values**: - /// 1. The `Motd` for the current connection. - Motd(Motd), - /// Force the raknet server to invoke `panic!`. - /// - /// **Tuple Values**: - /// 1. The message passed to `panic!` - Error(String), - /// Force the current client to disconnect. - /// This does **NOT** emit a disonnect event. - /// **Tuple Values**: - /// 1. The reason for disconnect (if any). - Disconnect(String), -} - -pub struct RakNetServer { - pub address: String, - pub version: RakNetVersion, - pub connections: Arc>>, - pub start_time: SystemTime, - pub server_guid: u64, - pub stop: bool, -} - -impl RakNetServer { - pub fn new(address: String) -> Self { - Self { - address, - version: RakNetVersion::V10, - connections: Arc::new(RwLock::new(HashMap::new())), - start_time: SystemTime::now(), - server_guid: rand::random::(), - stop: false, - } - } -} - -pub async fn start<'a>( - s: RakNetServer, - send_channel: Channel<'a, RakEvent, RakResult>, -) -> ( - impl Future + 'a, - Arc, - tokio::sync::mpsc::Sender<(String, Vec, bool)>, -) { - // The actual server reference. - let server = Arc::new(s); - // The reference to the server for the sending thread. - // This thread is responsible for ticking the client and - // dispatching client events every tick. - let send_server = server.clone(); - // The reference to the server for the for sending packets. - // This is the task that is used to send packets to the clients - // from the api. This mspc channel is return to the user. - let task_server = send_server.clone(); - // The reference to the server for returning the raknet server. - // While the sender should already have this, the server does become - // owned and pushed out of scope after execution. - let ret_server = send_server.clone(); - let sock = UdpSocket::bind( - server - .address - .parse::() - .expect("Failed to bind to address."), - ) - .await - .unwrap(); - let port = server.address.parse::().unwrap().port(); - // The socket of the server for sending packets (ticking client thread). - let send_sock = Arc::new(sock); - // The socket for the recieving thread. - let socket = send_sock.clone(); - // The socket for the internal server sending thread. - let send_sock_internal = send_sock.clone(); - // The time we're going to say raknet actually started. - let start_time = server.start_time.clone(); - // The id of the server - let server_id = server.server_guid.clone(); - // The server of the server - let version = server.version.clone(); - // The channels being used to send packets to the client (externally). - let (send, mut recv) = tokio::sync::mpsc::channel::<(String, Vec, bool)>(2048); - // The internal channels being used to dispatch packets with `connection.send`. - let (im_send, mut im_recv) = tokio::sync::mpsc::channel::<(String, Vec)>(2048); - - let tasks = async move { - // This task is solely responsible for internal immediate sending. - // Nothing else, this is not used externally, nor should it be. - tokio::spawn(async move { - loop { - if let Some(data) = im_recv.recv().await { - if let Ok(_) = send_sock_internal - .send_to(&data.1, from_address_token(data.0)) - .await - { - continue; - } else { - rak_debug!("Failed to send immediate packet."); - } - } - } - }); - - tokio::spawn(async move { - loop { - if let Some((address, buf, instant)) = recv.recv().await { - let mut clients = task_server.connections.write().unwrap(); - if clients.contains_key(&address) { - let client = clients.get_mut(&address).unwrap(); - client.send_stream( - buf, - if instant { - SendPriority::Immediate - } else { - SendPriority::Normal - }, - ); - drop(client); - drop(clients); - } else { - println!("ERR: Client not found: {}", address); - drop(clients); - } - } - } - }); - - tokio::spawn(async move { - let internal_send = Arc::new(im_send); - loop { - if let Err(_) = socket.readable().await { - continue; - }; - - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf).await { - let data = &buf[..len]; - let address_token = to_address_token(addr); - - // // rak_debug!("[RakNet] [{}] Received packet: Packet(ID={:#04x})", addr, &data[0]); - - if let Ok(mut clients) = server.connections.write() { - if let Some(c) = clients.get_mut(&address_token) { - c.recv(&data.to_vec()); - } else { - // add the client! - // we need to add cooldown here eventually. - if !clients.contains_key(&address_token) { - let mut c = Connection::new( - address_token.clone(), - internal_send.clone(), - start_time, - server_id, - port.to_string(), - version.clone(), - ); - c.recv(&data.to_vec()); - clients.insert(address_token, c); - } else { - // throw an error, this should never happen. - } - } - } - } else { - // log error in future! - // rak_debug!("[RakNet] Unknown error decoding packet!"); - continue; - } - } - }); - - while !&send_server.stop { - if let Err(_) = send_sock.writable().await { - continue; - }; - - // sleep an entire tick - sleep(Duration::from_millis(50)).await; - - let mut clients = send_server.connections.write().unwrap(); - for (addr, _) in clients.clone().iter() { - let client = clients.get_mut(addr).expect("Could not get connection"); - client.tick(); - - let dispatch = client.event_dispatch.clone(); - client.event_dispatch.clear(); - - // emit events if there is a listener for the - for event in dispatch.iter() { - // // rak_debug!("DEBUG => Dispatching: {:?}", &event.get_name()); - if let Some(result) = send_channel.send(event.clone()) { - match result { - RakResult::Motd(v) => { - client.motd = v; - } - RakResult::Error(v) => { - // Calling error forces an error to raise. - panic!("{}", v); - } - RakResult::Disconnect(_) => { - client.state = ConnectionState::Offline; // simple hack - break; - } - } - } - } - - // Forcefully remove the client if they are offline. - // This is after the packet sending because we may want to send packets if - // the disconnect notification is server sided. - if client.is_disconnected() { - clients.remove(addr); - continue; - } - - if client.queue.clone().len() == 0 { - continue; - } - - let packets = client.queue.flush(); - - for pk in packets.into_iter() { - match send_sock - .send_to(&pk[..], &from_address_token(addr.clone())) - .await - { - // Add proper handling! - Err(e) => rak_debug!("[RakNet] [{}] Error sending packet: {}", addr, e), - Ok(_) => { - if client.state.is_connected() { - if cfg!(any(test, feature = "dbg-verbose")) { - rak_debug!( - "[ONLINE PACKET] [{}] Sent packet: {:?}\n", - addr, - &pk - ); - } else { - rak_debug!( - "[ONLINE PACKET] [{}] Sent packet: {}", - addr, - *pk.get(0).unwrap_or(&0) - ); - } - } else { - rak_debug!( - "[OFFLINE] [{}] Sent packet: {}", - addr, - *pk.get(0).unwrap_or(&0) - ); - } - } - } - } - } - drop(clients); - } - }; - - return (tasks, ret_server, send); -} diff --git a/test/Cargo.toml b/test/Cargo.toml deleted file mode 100644 index 574f19a..0000000 --- a/test/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "rakrs-test" -version = "1.0.0" -authors = ["Bavfalcon9"] -edition = "2021" -private = true - -[dependencies] -rakrs = { path = "../", features = ["mcpe", "debug"] } -tokio = { version = "1.15.0", features = ["full"] } -netrex_events = { git = "https://github.com/NetrexMC/Events", branch = "master" } diff --git a/test/src/main.rs b/test/src/main.rs deleted file mode 100644 index 1c82786..0000000 --- a/test/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -use rakrs::start; -use rakrs::RakEvent; -use rakrs::RakNetServer; -use rakrs::RakResult; -use tokio::runtime::Runtime; - -#[tokio::main] -pub async fn main() { - let server = RakNetServer::new(String::from("0.0.0.0:19132")); - let channel = netrex_events::Channel::::new(); - let mut unknown = 0; - let mut listener = |event, _| { - match event { - RakEvent::ConnectionCreated(address) => { - println!("[RakNet] [{}] Client connected", address); - } - RakEvent::Disconnect(address, reason) => { - println!( - "[RakNet] [{}] Client disconnected due to: {}", - address, reason - ); - } - RakEvent::Motd(address, mut motd) => { - // println!("[RakNet] [{}] Client requested motd: {:?}", address, motd); - motd.name = String::from("RakRS v2"); - return Some(RakResult::Motd(motd)); - } - RakEvent::GamePacket(address, packet) => { - println!("[RakNet] [{}] Client sent packet: {:?}", address, packet); - return None; - } - _ => { - unknown += 1; - println!("Unknown events: {}", unknown); - } - }; - None - }; - channel.receive(&mut listener); - - let v = start(server, channel).await; - v.0.await; -} \ No newline at end of file diff --git a/tests/defaults.rs b/tests/defaults.rs deleted file mode 100644 index ae6908c..0000000 --- a/tests/defaults.rs +++ /dev/null @@ -1,7 +0,0 @@ -use rakrs::protocol::mcpe::motd::Motd; - -#[test] -fn run_test() { - let motd = Motd::new(12, 19132.to_string()); - assert_eq!(motd.port, 19132.to_string()); -} diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index 59595b2..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod defaults; From 884de387f8eb143c37820af896af986f6e5dc963 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 23 Oct 2022 17:55:57 -0500 Subject: [PATCH 02/75] feat: Add queues, states and the cache store --- .github/workflows/devskim-analysis.yml | 22 ------ .gitignore | 1 + Cargo.toml | 1 - coverage.py | 3 + src/conn/mod.rs | 11 ++- src/conn/queue.rs | 14 ++-- src/conn/state.rs | 93 ++++++++++++++++++++++++++ src/lib.rs | 3 +- src/server/mod.rs | 8 +-- src/util/mod.rs | 66 ++++++++++++++++++ 10 files changed, 183 insertions(+), 39 deletions(-) delete mode 100644 .github/workflows/devskim-analysis.yml create mode 100644 coverage.py create mode 100644 src/conn/state.rs create mode 100644 src/util/mod.rs diff --git a/.github/workflows/devskim-analysis.yml b/.github/workflows/devskim-analysis.yml deleted file mode 100644 index 3263d8e..0000000 --- a/.github/workflows/devskim-analysis.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: DevSkim - -on: - schedule: - - cron: '00 09 * * 1' - -jobs: - lint: - name: DevSkim - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - uses: actions/checkout@v2 - - name: Run DevSkim scanner - uses: microsoft/DevSkim-Action@v1 - - name: Upload DevSkim scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: devskim-results.sarif diff --git a/.gitignore b/.gitignore index 6825043..bc8a328 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target Cargo.lock *.old/ +*.profraw \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e7b2717..8f31ca7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -netrex_events = { git = "https://github.com/NetrexMC/Events", branch = "master" } tokio = { version = "1.15.0", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" diff --git a/coverage.py b/coverage.py new file mode 100644 index 0000000..a0a1b7b --- /dev/null +++ b/coverage.py @@ -0,0 +1,3 @@ +import os +os.system("CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo test") +os.system("gcov . --binary-path ./target/debug/deps -s . -t html --branch --ignore-not-existing --ignore ../* --ignore /* --ignore xtask/* --ignore */src/tests/* -o coverage/html") \ No newline at end of file diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 0e39d0a..780272d 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -1,6 +1,7 @@ pub mod queue; +pub mod state; -use std::{net::SocketAddr, sync::Arc}; +use std::{net::SocketAddr, sync::{Arc, Mutex}}; use tokio::sync::RwLock; @@ -17,4 +18,10 @@ pub struct Conn { /// The queue used to send packets back to the connection. pub(crate) send_queue: Arc>, -} \ No newline at end of file + + /// The queue used to recieve packets, this is read from by the server. + /// This is only used internally. + pub(crate) recv_queue: Arc>, + + pub(crate) state: state::ConnState +} diff --git a/src/conn/queue.rs b/src/conn/queue.rs index a4d8d76..4eed158 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -4,7 +4,8 @@ use std::collections::HashMap; /// within a reliable window time. /// /// Usage: -/// ```rust no_run +/// ```rust +/// use rakrs::conn::queue::OrderedQueue; /// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); /// // Insert a packet with the id of "1" /// ord_qu.insert(vec![0, 1], 1); @@ -12,8 +13,8 @@ use std::collections::HashMap; /// ord_qu.insert(vec![2, 0], 3); /// /// // Get the packets we still need. -/// let needed: Vec = ord.qu.flush_missing(); -/// assert_eq!(needed, vec![2, 4]); +/// let needed: Vec = ord_qu.flush_missing(); +/// assert_eq!(needed, vec![0, 2, 4]); /// /// // We would in theory, request these packets, but we're going to insert them /// ord_qu.insert(vec![2, 0, 0, 1], 4); @@ -21,7 +22,7 @@ use std::collections::HashMap; /// /// // Now let's return our packets in order. /// // Will return a vector of these packets in order by their "id". -/// let ordered: Vec> = ord.qu.flush(); +/// let ordered: Vec> = ord_qu.flush(); /// ``` #[derive(Debug)] pub struct OrderedQueue { @@ -126,7 +127,6 @@ where /// This queue is used to prioritize packets being sent out /// Packets that are old, are either dropped or requested again, /// you can define this behavior with the `timeout` property. -/// ! This API replaces `CacheStore` for ack channels #[derive(Debug, Clone)] pub struct SendQueue { /// The amount of time that needs to pass for a packet to be @@ -141,6 +141,4 @@ pub struct SendQueue { /// a packet is sent reliably. We can resend these if they are /// Acked. send_seq: u32, - - -} +} \ No newline at end of file diff --git a/src/conn/state.rs b/src/conn/state.rs new file mode 100644 index 0000000..1d7cb33 --- /dev/null +++ b/src/conn/state.rs @@ -0,0 +1,93 @@ +/// Connection States +/// These are all possible states of a raknet session, and while accessible externally +/// Please note that these are not states relied in the original implementation of +/// raknet, which preserve both "Unconnected" and "Connected" +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum ConnState { + /// The Session is not yet connected, but is actively trying to connect. + /// Clients in this state are considered to be actively trying to connect. + Connecting, + + /// The Session is connected and ready to send and receive packets. + /// This is the state after a connection has been established. + /// + /// This state is applied once the `ConnectionHandshake` has been completed. + Connected, + + /// The session is being timed out because it has not sent a packet in a while. + /// The interval for this can be set in the Session Options. + TimingOut, + + /// The session has been disconnected but is still in the process of cleaning up. + /// This is the state after a disconnect has been requested, but the client still wants + /// to send packets until its done. + Disconnecting, + + /// The session has been disconnected and is ready to be removed. + /// This is the state after a disconnect has been requested and the client has + /// This is almost never used. + Disconnected, + + /// The session is replying to the server but is not actually connected. This is + /// the state where ping and pong packets are being sent. Similarly, this is + /// the "Unconnected" state, hence "UnconnectedPing" + Unidentified, + + /// The session is not connected and is not trying to connect. + /// During this state the session will be dropped. This state occurs when a client + /// has completely stopped responding to packets or their socket is destroyed. + /// This is not the same as the [Disconnected](rakrs::conn::state::Disconnected) state. + Offline, +} + +impl ConnState { + /// Returns whether or not the Session is reliable. + /// Reliable sessions are sessions that are not: + /// - Offline + /// - Disconnected + /// - TimingOut + pub fn is_reliable(&self) -> bool { + match self { + Self::Disconnected | Self::TimingOut | Self::Offline => false, + _ => true, + } + } + + /// Returns whether or not the Session is available to recieve + /// packets. Sessions in this state are: + /// - Connected + /// - Connecting + /// - Unidentified + /// - Disconnecting + pub fn is_available(&self) -> bool { + match self { + Self::Connected | Self::Connecting | Self::Unidentified | Self::Disconnecting => true, + _ => false, + } + } + + /// Returns whether or not the Session is in any "connected" state. + /// Sessions in this state are: + /// - Connected + /// - Connecting + pub fn is_connected(&self) -> bool { + match self { + Self::Connected | Self::Connecting => true, + _ => false, + } + } +} + +impl std::fmt::Display for ConnState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Connecting => write!(f, "Connecting"), + Self::Connected => write!(f, "Connected"), + Self::TimingOut => write!(f, "TimingOut"), + Self::Disconnecting => write!(f, "Disconnecting"), + Self::Disconnected => write!(f, "Disconnected"), + Self::Unidentified => write!(f, "Unidentified"), + Self::Offline => write!(f, "Offline"), + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d42ef61..427bee5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ +pub mod conn; pub mod server; -pub mod conn; \ No newline at end of file +pub mod util; \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index fb42687..ae54cd9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, net::SocketAddr}; +use std::{net::SocketAddr, sync::Arc}; use tokio::net::UdpSocket; @@ -14,7 +14,5 @@ pub struct Listener { } impl Listener { - pub async fn bind(address: SocketAddr) { - - } -} \ No newline at end of file + pub async fn bind(address: SocketAddr) {} +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..62e125f --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,66 @@ +use std::{collections::HashMap, time::SystemTime}; + +/// This is a fancy wrapper over a HashMap that serves as +/// a time oriented cache, where you can optionally clean up +/// old and un-used values. Key serves as a `packet_id` in +/// rakrs, but this could be used else-where. +/// +/// Usage example: +/// ```rust +/// use rakrs::util::CacheStore; +/// +/// let mut myStore: CacheStore> = CacheStore::new(); +/// let myPacket = (0 as u8, vec![0, 0, 0, 1]); +/// myStore.add(myPacket.0, myPacket.1); +/// // Wait a few seconds +/// myStore.flush(); +/// ``` +#[derive(Debug, Clone)] +pub struct CacheStore { + pub(crate) store: HashMap)>, +} + +impl CacheStore +where + K: std::hash::Hash + std::cmp::Eq, + V: ?Sized + Clone, +{ + pub fn new() -> Self { + Self { + store: HashMap::new(), + } + } + + pub fn add(&mut self, sequence: K, buffer: V) { + let ent = self + .store + .entry(sequence) + .or_insert((SystemTime::now(), Vec::new())); + ent.1.push(buffer); + } + + pub fn add_bulk(&mut self, sequence: K, buffers: Vec) { + let ent = self + .store + .entry(sequence) + .or_insert((SystemTime::now(), Vec::new())); + ent.1.extend(buffers); + } + + // clear old packets from the cache and return them + pub fn flush(&mut self) -> Vec<(K, SystemTime, Vec)> { + let mut flushed = Vec::new(); + for (sequence, (time, frames)) in self.store.drain() { + flushed.push((sequence, time, frames)); + } + return flushed; + } + + pub fn flush_key(&mut self, key: K) -> Option<(SystemTime, Vec)> { + self.store.remove(&key) + } + + pub fn has(&self, key: &K) -> bool { + self.store.contains_key(key) + } +} From 92bbf42a12d1ce36872df3373c8484b14cfcc133 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 30 Oct 2022 21:53:29 -0500 Subject: [PATCH 03/75] chore: Add existing packets from previous version --- src/conn/mod.rs | 2 +- src/conn/queue.rs | 9 +- src/lib.rs | 3 +- src/protocol/frame.rs | 321 ++++++++++++++++++++++++++++++ src/protocol/magic.rs | 36 ++++ src/protocol/mcpe/mod.rs | 21 ++ src/protocol/mcpe/motd.rs | 204 +++++++++++++++++++ src/protocol/mod.rs | 7 + src/protocol/packet/mod.rs | 352 +++++++++++++++++++++++++++++++++ src/protocol/packet/offline.rs | 133 +++++++++++++ src/protocol/packet/online.rs | 134 +++++++++++++ src/protocol/reliability.rs | 95 +++++++++ 12 files changed, 1313 insertions(+), 4 deletions(-) create mode 100644 src/protocol/frame.rs create mode 100644 src/protocol/magic.rs create mode 100644 src/protocol/mcpe/mod.rs create mode 100644 src/protocol/mcpe/motd.rs create mode 100644 src/protocol/mod.rs create mode 100644 src/protocol/packet/mod.rs create mode 100644 src/protocol/packet/offline.rs create mode 100644 src/protocol/packet/online.rs create mode 100644 src/protocol/reliability.rs diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 780272d..81f3810 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -5,7 +5,7 @@ use std::{net::SocketAddr, sync::{Arc, Mutex}}; use tokio::sync::RwLock; -use self::queue::SendQueue; +use self::queue::{SendQueue, RecvQueue}; /// This struct is utilized internally and represented /// as per each "connection" or "socket" to the server. diff --git a/src/conn/queue.rs b/src/conn/queue.rs index 4eed158..4f1d57c 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -125,8 +125,8 @@ where } /// This queue is used to prioritize packets being sent out -/// Packets that are old, are either dropped or requested again, -/// you can define this behavior with the `timeout` property. +/// Packets that are old, are either dropped or requested again. +/// You can define this behavior with the `timeout` property. #[derive(Debug, Clone)] pub struct SendQueue { /// The amount of time that needs to pass for a packet to be @@ -141,4 +141,9 @@ pub struct SendQueue { /// a packet is sent reliably. We can resend these if they are /// Acked. send_seq: u32, +} + +#[derive(Debug, Clone)] +pub struct RecvQueue { + } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 427bee5..28f2360 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ pub mod conn; pub mod server; -pub mod util; \ No newline at end of file +pub mod util; +pub mod protocol; \ No newline at end of file diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs new file mode 100644 index 0000000..22c968f --- /dev/null +++ b/src/protocol/frame.rs @@ -0,0 +1,321 @@ +use std::io::{Cursor, Write}; + +use binary_utils::error::BinaryError; +use binary_utils::*; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; + +/// The information for the given fragment. +/// This is used to determine how to reassemble the frame. +#[derive(Debug, Clone)] +pub struct FragmentMeta { + /// The total number of fragments in this frame. + pub(crate) size: u32, + /// The identifier for this fragment. + /// This is used similar to a ordered channel, where the trailing buffer + /// will be stored with this identifier. + pub(crate) id: u16, + /// The index of the fragment. + /// This is the arrangement of the fragments in the frame. + pub(crate) index: u32, +} + +use super::reliability::Reliability; + +/// Frames are a encapsulation of a packet or packets. +/// They are used to send packets to the connection in a reliable way. +#[derive(Debug, Clone)] +pub struct FramePacket { + /// The sequence of this frame. + /// We'll use this to respond with Ack and Nack to. + /// This is sized check to 24 bits. + pub sequence: u32, + + /// The frames for this frame packet, not to exceed the mtu size. + pub frames: Vec, + + /// This is internal use only. + pub(crate) reliability: Reliability, + + /// This is internal use only. + pub(crate) byte_length: usize, +} + +impl FramePacket { + /// Creates an empty frame packet. + pub fn new() -> Self { + Self { + sequence: 0, + frames: Vec::new(), + reliability: Reliability::ReliableOrd, + byte_length: 0, + } + } + + /// Paritions a stream into a bunch of fragments and returns a frame packet + /// that is partitioned, otherwise known as "fragmented". + /// This does not modify reliability. That is up to the caller. + pub fn partition(stream: Vec, id: u16, frag_size: u32) -> Vec { + let mut meta: FragmentMeta = FragmentMeta { + size: 0, + id, + index: 0, + }; + + let mut frames: Vec = Vec::new(); + let mut position: usize = 0; + + while position < stream.len() { + // check whether or not we can read the rest of of the stream + if stream[position..].len() < frag_size as usize { + // we can reliably read the rest of the buffer into a single frame. + let mut frame = Frame::init(); + frame.body = stream[position..].to_vec(); + frame.fragment_meta = Some(meta.clone()); + frames.push(frame); + break; + } else { + // we can't read the rest of the stream into a single frame + // continue to split into multiple frames. + let mut frame = Frame::init(); + let to_pos = position + (frag_size as usize); + frame.body = stream[position..to_pos].to_vec(); + frame.fragment_meta = Some(meta.clone()); + frames.push(frame); + position = to_pos; + meta.index += 1; + } + } + + meta.size = frames.len() as u32; + + // Let's fix up the meta data in each frame. + for frame in frames.iter_mut() { + if let Some(m) = frame.fragment_meta.as_mut() { + m.size = meta.size; + } + } + + return frames; + } +} + +impl Streamable for FramePacket { + fn compose(source: &[u8], position: &mut usize) -> Result { + let mut stream = Cursor::new(source); + stream.set_position(*position as u64); + stream.read_u8()?; + let mut frames: Vec = Vec::new(); + let sequence = stream.read_u24::()?; + let mut offset: usize = stream.position() as usize; + + loop { + if stream.position() > source.len() as u64 { + return Ok(FramePacket { + reliability: Reliability::ReliableOrd, + sequence, + frames, + byte_length: 0, + }); + } + + if stream.position() == source.len() as u64 { + break Ok(FramePacket { + reliability: Reliability::ReliableOrd, + sequence, + frames, + byte_length: 0, + }); + } + + if let Ok(frame) = Frame::compose(&source, &mut offset) { + stream.set_position(offset as u64); + frames.push(frame.clone()); + + // if frame.parse()?.len() + stream.position() as usize > source.len() { + // return Ok(FramePacket { sequence, frames, byte_length: 0 }); + // } else { + // continue; + // } + } else { + return Err(BinaryError::RecoverableKnown( + "Frame composition failed! Failed to read frame.".into(), + )); + } + } + } + + fn parse(&self) -> Result, BinaryError> { + let mut stream = Cursor::new(Vec::new()); + stream.write_u8(0x80)?; + stream.write_u24::(self.sequence)?; + + for frame in &self.frames { + stream.write_all(&frame.parse()?)?; + } + + Ok(stream.into_inner()) + } +} + +/// An individual data frame, these are constructed from a payload. +#[derive(Debug, Clone)] +pub struct Frame { + /// The flags for this frame, the first 3 bits are reserved for the reliability while the 4th + /// bit is used to represent if this is a fragment. + pub flags: u8, + /// The length of the body of the frame. + /// This is sized to 24 bits internally, so any number here must be within that range. + pub size: u16, + /// The Reliable index of the frame (if reliable) + pub reliable_index: Option, + /// The sequenced index of the frame (if sequenced) + /// This is used to determine the position in frame list. + pub sequence_index: Option, + /// The order index of the frame (if ordered) + /// This is used to determine the position in frame list, + /// This is different from the sequence index in that it is + /// used more to sequence packets in a specific manner. + pub order_index: Option, + /// The order channel of the frame (if ordered) + /// This is used to store order information for the frame. + pub order_channel: Option, + /// The information for fragmentation (if the frame is split into parts) + /// This is used to determine how to reassemble the frame. + pub fragment_meta: Option, + /// The reliability of this frame, this is essentially used to save frames and send them back if + /// they are lost. Otherwise, the frame is sent unreliably. + pub reliability: Reliability, + /// The body of the frame, this is the payload of the frame. + pub body: Vec, +} + +impl Frame { + /// Initializes a new empty frame that is Unreliable. + /// This is usually used to inject data into. + pub fn init() -> Self { + Self { + flags: 0, + size: 0, + reliable_index: None, + sequence_index: None, + order_index: None, + order_channel: None, + fragment_meta: None, + reliability: Reliability::Unreliable, + body: Vec::new(), + } + } + + /// Whether or not the frame is fragmented. + pub fn is_fragmented(&self) -> bool { + self.fragment_meta.is_some() + } + + /// Whether or not the frame is sequenced and reliable. + pub fn is_sequenced(&self) -> bool { + self.reliability.is_sequenced() + } +} + +impl Streamable for Frame { + fn compose(source: &[u8], position: &mut usize) -> Result { + let mut stream = Cursor::new(source.to_vec()); + + // create a dummy frame for us to write to. + let mut frame: Frame = Frame::init(); + + // set the position to the current position + stream.set_position(*position as u64); + + // read the flags + frame.flags = stream.read_u8()?; + // set the reliability + frame.reliability = Reliability::from_flags(frame.flags); + + // read the length of the body in bits + frame.size = stream.read_u16::()? / 8; + + // check whether or not this frame is reliable, if it is, read the reliable index + if frame.reliability.is_reliable() { + frame.reliable_index = Some(stream.read_u24::()?); + } + + // check whether or not this frame is sequenced, if it is, read the sequenced index + if frame.reliability.is_sequenced() { + frame.sequence_index = Some(stream.read_u24::()?); + } + + // check whether or not this frame is ordered, if it is, read the order index + // and order channel + if frame.reliability.is_sequenced_or_ordered() { + frame.order_index = Some(stream.read_u24::()?); + frame.order_channel = Some(stream.read_u8()?); + } + + // check whether or not this frame is fragmented, if it is, read the fragment meta + if (frame.flags & 0x10) > 0 { + frame.fragment_meta = Some(FragmentMeta { + size: stream.read_u32::()?.try_into().unwrap(), + id: stream.read_u16::()?, + index: stream.read_u32::()?.try_into().unwrap(), + }); + } + + // read the body + frame.body = (&source + [stream.position() as usize..stream.position() as usize + frame.size as usize]) + .to_vec(); + // update the position. + *position = stream.position() as usize + frame.size as usize; + + Ok(frame) + } + + fn parse(&self) -> Result, error::BinaryError> { + let mut stream = Cursor::new(Vec::new()); + // generate the flags! + let mut flags = self.reliability.to_flags(); + + // check whether or not this frame is fragmented, if it is, set the fragment flag + if self.fragment_meta.is_some() { + flags |= 0x10; + } + + let size = self.body.len() as u16; + + // write the flags + stream.write_u8(flags)?; + // write the length of the body in bits + stream.write_u16::(size * 8)?; + + // check whether or not this frame is reliable, if it is, write the reliable index + if self.reliability.is_reliable() { + stream.write_u24::(self.reliable_index.unwrap())?; + } + + // check whether or not this frame is sequenced, if it is, write the sequenced index + if self.reliability.is_sequenced() { + stream.write_u24::(self.sequence_index.unwrap())?; + } + + // check whether or not this frame is ordered, if it is, write the order index + // and order channel + if self.reliability.is_sequenced_or_ordered() { + stream.write_u24::(self.order_index.unwrap())?; + stream.write_u8(self.order_channel.unwrap())?; + } + + // check whether or not this frame is fragmented, if it is, write the fragment meta + if self.fragment_meta.is_some() { + let fragment_meta = self.fragment_meta.as_ref().unwrap(); + stream.write_u32::(fragment_meta.size.try_into().unwrap())?; + stream.write_u16::(fragment_meta.id)?; + stream.write_u32::(fragment_meta.index.try_into().unwrap())?; + } + + // write the body + stream.write_all(&self.body)?; + + Ok(stream.get_ref().clone()) + } +} diff --git a/src/protocol/magic.rs b/src/protocol/magic.rs new file mode 100644 index 0000000..85d1499 --- /dev/null +++ b/src/protocol/magic.rs @@ -0,0 +1,36 @@ +use binary_utils::{error::BinaryError, Streamable}; + +/// A unique identifier recoginzing the client as offline. +pub(crate) const MAGIC: [u8; 16] = [ + 0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, +]; + +#[derive(Debug, Clone)] +pub struct Magic(pub Vec); + +impl Magic { + pub fn new() -> Self { + Self(MAGIC.to_vec()) + } +} + +impl Streamable for Magic { + fn parse(&self) -> Result, BinaryError> { + Ok(MAGIC.to_vec()) + } + + fn compose(source: &[u8], position: &mut usize) -> Result { + // magic is 16 bytes + let pos = *position + (16 as usize); + let magic = &source[*position..pos]; + *position += 16; + + if magic.to_vec() != MAGIC.to_vec() { + Err(BinaryError::RecoverableKnown( + "Could not construct magic from malformed bytes.".to_string(), + )) + } else { + Ok(Self(magic.to_vec())) + } + } +} diff --git a/src/protocol/mcpe/mod.rs b/src/protocol/mcpe/mod.rs new file mode 100644 index 0000000..8881e83 --- /dev/null +++ b/src/protocol/mcpe/mod.rs @@ -0,0 +1,21 @@ +/// Minecraft has specific protocol for the `UnconnectedPong` packet. +/// This data is attached to the Unconnect Pong packet and is used to +/// display information about the server. +pub mod motd; + +use crate::packet_id; + +use binary_utils::*; + +use self::motd::Motd; + +use super::{packet::PacketId, Magic}; + +#[derive(Debug, Clone, BinaryStream)] +pub struct UnconnectedPong { + pub timestamp: u64, + pub server_id: u64, + pub magic: Magic, + pub motd: Motd, +} +packet_id!(UnconnectedPong, 0x1c); diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs new file mode 100644 index 0000000..36d0f4a --- /dev/null +++ b/src/protocol/mcpe/motd.rs @@ -0,0 +1,204 @@ +use binary_utils::Streamable; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Gamemode { + Survival = 0, + Creative, + Adventure, + Spectator, +} + +impl Gamemode { + pub fn as_str(&self) -> &'static str { + match self { + Gamemode::Survival => "Survival", + Gamemode::Creative => "Creative", + Gamemode::Adventure => "Adventure", + Gamemode::Spectator => "Spectator", + } + } +} + +impl std::fmt::Display for Gamemode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let v = match *self { + Gamemode::Survival => "0", + Gamemode::Creative => "1", + Gamemode::Adventure => "2", + Gamemode::Spectator => "3", + }; + write!(f, "{}", v) + } +} + +/// Protocol wise, motd is just a string +/// However we're using this struct to represent the motd +#[derive(Debug, Clone)] +pub struct Motd { + /// The name of the server + pub name: String, + /// The protocol version + pub protocol: u16, + /// The version of the server + pub version: String, + /// The number of players online + pub player_count: u16, + /// The maximum number of players + pub player_max: u16, + /// The gamemode of the server + pub gamemode: Gamemode, + /// The server's GUID + pub server_guid: u64, + /// The server's port + pub port: String, + /// The IPv6 port + /// TODO: Implement this + pub ipv6_port: String, +} + +impl Motd { + pub fn new>(server_guid: u64, port: S) -> Self { + Self { + name: "Netrex Server".into(), + player_count: 10, + player_max: 100, + protocol: 448, + gamemode: Gamemode::Survival, + version: "1.18.0".into(), + server_guid, + port: port.into(), + ipv6_port: "19133".into(), + } + } + + /// Takes the Motd and parses it into a valid MCPE + /// MOTD buffer. + pub fn write(&self) -> String { + let props: Vec = vec![ + "MCPE".into(), + self.name.clone(), + self.protocol.to_string(), + self.version.clone(), + self.player_count.to_string(), + self.player_max.to_string(), + self.server_guid.to_string(), + "Netrex".to_string(), + self.gamemode.as_str().to_string(), + "1".to_string(), + // Todo: Figure out why this is not working + // self.gamemode.to_string(), + self.port.to_string(), + self.ipv6_port.to_string(), + ]; + props.join(";").into() + } +} + +impl Streamable for Motd { + fn compose( + source: &[u8], + position: &mut usize, + ) -> Result { + let motd = String::compose(source, position)?; + let parts = motd + .split(";") + .map(|c| c.to_string()) + .collect::>(); + let name = parts + .get(1) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd name".into(), + ))?; + let protocol = parts + .get(2) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd protocol".into(), + ))?; + let version = parts + .get(3) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd version".into(), + ))?; + let player_count = + parts + .get(4) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd player count".into(), + ))?; + let player_max = parts + .get(5) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd player maxmium".into(), + ))?; + let server_guid = + parts + .get(6) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd server guid".into(), + ))?; + let _ = parts + .get(7) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd software name".into(), + ))?; + let _ = parts + .get(8) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd gamemode string".into(), + ))?; + let gamemode = parts + .get(9) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd gamemode".into(), + ))?; + let port = parts + .get(10) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd port".into(), + ))?; + let ipv6_port = parts + .get(11) + .ok_or(binary_utils::error::BinaryError::RecoverableKnown( + "Invalid motd port".into(), + ))?; + + Ok(Motd { + name: name.clone(), + protocol: protocol + .as_str() + .parse::() + .expect("Invalid motd protocol"), + version: version.clone(), + player_count: player_count + .as_str() + .parse::() + .expect("Player count is not a number"), + player_max: player_max + .as_str() + .parse::() + .expect("Player Maximum is not a number"), + server_guid: server_guid + .as_str() + .parse::() + .expect("Server GUID is not a number"), + port: port.clone(), + ipv6_port: ipv6_port.clone(), + gamemode: match gamemode + .as_str() + .parse::() + .expect("Gamemode is not a byte") + { + 0 => Gamemode::Survival, + 1 => Gamemode::Creative, + 2 => Gamemode::Adventure, + 3 => Gamemode::Spectator, + _ => Gamemode::Survival, + }, + }) + } + + fn parse(&self) -> Result, binary_utils::error::BinaryError> { + self.write().parse() + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000..63078c8 --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod frame; +pub(crate) mod packet; +pub(crate) mod magic; +pub(crate) mod mcpe; +pub mod reliability; + +pub use magic::*; \ No newline at end of file diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs new file mode 100644 index 0000000..d30a7b1 --- /dev/null +++ b/src/protocol/packet/mod.rs @@ -0,0 +1,352 @@ +// /// Handlers for both online & offline packets! +// /// This is used by the connection struct to handle packets. +// pub(crate) mod handler; + +/// The protocol that is used when a client is considered "Online". +/// Sessions in this state are usually: Connected or Connecting +pub mod online; + +/// The protocol that is used when a client is considered "Unidentified". +/// This module is used for Pong and connection requests. +pub mod offline; + +use std::io::Write; + +use binary_utils::Streamable; +use byteorder::WriteBytesExt; + +use self::offline::{ + IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, + SessionInfoRequest, UnconnectedPing, UnconnectedPong, +}; +use self::online::{ + ConnectedPing, ConnectedPong, ConnectionAccept, ConnectionRequest, Disconnect, LostConnection, + NewConnection, +}; + +use self::offline::OfflinePacket; +use self::online::OnlinePacket; + +/// A helper trait to identify packets. +pub trait PacketId { + fn id() -> u8; + + fn get_id(&self) -> u8 { + Self::id() + } +} + +/// A Generic Packet. +/// This is the base for all packets. +#[derive(Clone, Debug)] +pub struct Packet { + /// The packet id. + pub id: u8, + /// The packet data. (this is the payload) + pub payload: Payload, +} + +impl Packet { + pub fn is_online(&self) -> bool { + match self.payload { + Payload::Online(_) => true, + _ => false, + } + } + + pub fn is_offline(&self) -> bool { + return !self.is_online(); + } + + pub fn get_online(&self) -> OnlinePacket { + self.clone().into() + } + + pub fn get_offline(&self) -> OfflinePacket { + self.clone().into() + } +} + +impl Streamable for Packet { + fn compose( + source: &[u8], + position: &mut usize, + ) -> Result { + let payload = Payload::compose(source, position)?; + let id = source[0]; + Ok(Packet { id, payload }) + } + + fn parse(&self) -> Result, binary_utils::error::BinaryError> { + let mut buffer: Vec = Vec::new(); + buffer.write_u8(self.id)?; + buffer.write_all(&self.payload.parse()?)?; + Ok(buffer) + } +} + +#[derive(Clone, Debug)] +pub enum Payload { + Online(OnlinePacket), + Offline(OfflinePacket), +} + +impl Streamable for Payload { + fn compose( + source: &[u8], + position: &mut usize, + ) -> Result { + // we need the id! + let id = u8::compose(source, position)?; + + match id { + x if x == UnconnectedPing::id() => { + let packet = + OfflinePacket::UnconnectedPing(UnconnectedPing::compose(source, position)?); + Ok(Payload::Offline(packet)) + } + x if x == UnconnectedPong::id() => { + let packet = + OfflinePacket::UnconnectedPong(UnconnectedPong::compose(source, position)?); + Ok(Payload::Offline(packet)) + } + x if x == OpenConnectRequest::id() => { + let packet = OfflinePacket::OpenConnectRequest(OpenConnectRequest::compose( + source, position, + )?); + Ok(Payload::Offline(packet)) + } + x if x == OpenConnectReply::id() => { + let packet = + OfflinePacket::OpenConnectReply(OpenConnectReply::compose(source, position)?); + Ok(Payload::Offline(packet)) + } + x if x == SessionInfoRequest::id() => { + let packet = OfflinePacket::SessionInfoRequest(SessionInfoRequest::compose( + source, position, + )?); + Ok(Payload::Offline(packet)) + } + x if x == SessionInfoReply::id() => { + let packet = + OfflinePacket::SessionInfoReply(SessionInfoReply::compose(source, position)?); + Ok(Payload::Offline(packet)) + } + x if x == IncompatibleProtocolVersion::id() => { + let packet = OfflinePacket::IncompatibleProtocolVersion( + IncompatibleProtocolVersion::compose(source, position)?, + ); + Ok(Payload::Offline(packet)) + } + x if x == ConnectedPing::id() => { + let packet = OnlinePacket::ConnectedPing(ConnectedPing::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == ConnectedPong::id() => { + let packet = OnlinePacket::ConnectedPong(ConnectedPong::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == LostConnection::id() => { + let packet = + OnlinePacket::LostConnection(LostConnection::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == ConnectionRequest::id() => { + let packet = + OnlinePacket::ConnectionRequest(ConnectionRequest::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == ConnectionAccept::id() => { + let packet = + OnlinePacket::ConnectionAccept(ConnectionAccept::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == NewConnection::id() => { + let packet = OnlinePacket::NewConnection(NewConnection::compose(source, position)?); + Ok(Payload::Online(packet)) + } + x if x == Disconnect::id() => { + let packet = OnlinePacket::Disconnect(Disconnect::compose(source, position)?); + Ok(Payload::Online(packet)) + } + _ => Err(binary_utils::error::BinaryError::RecoverableKnown(format!( + "Id is not a valid raknet packet: {}", + id + ))), + } + } + + fn parse(&self) -> Result, binary_utils::error::BinaryError> { + let mut buffer: Vec = Vec::new(); + // we're not concerned about the id here so we're going to only write the payload! + let payload = match self { + Payload::Online(packet) => match packet { + OnlinePacket::ConnectedPing(pk) => pk.parse()?, + OnlinePacket::ConnectedPong(pk) => pk.parse()?, + OnlinePacket::LostConnection(pk) => pk.parse()?, + OnlinePacket::ConnectionRequest(pk) => pk.parse()?, + OnlinePacket::ConnectionAccept(pk) => pk.parse()?, + OnlinePacket::NewConnection(pk) => pk.parse()?, + OnlinePacket::Disconnect(pk) => pk.parse()?, + }, + Payload::Offline(packet) => match packet { + OfflinePacket::UnconnectedPing(pk) => pk.parse()?, + OfflinePacket::UnconnectedPong(pk) => pk.parse()?, + OfflinePacket::OpenConnectRequest(pk) => pk.parse()?, + OfflinePacket::OpenConnectReply(pk) => pk.parse()?, + OfflinePacket::SessionInfoRequest(pk) => pk.parse()?, + OfflinePacket::SessionInfoReply(pk) => pk.parse()?, + OfflinePacket::IncompatibleProtocolVersion(pk) => pk.parse()?, + }, + }; + if let Err(_) = buffer.write_all(&payload) { + Err(binary_utils::error::BinaryError::RecoverableKnown( + "Failed to write payload to buffer".to_string(), + )) + } else { + Ok(buffer) + } + } +} + +/// This allows implemenation for: +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::online::OnlinePacket; +/// let online: OnlinePacket = Packet::compose(source, position)?; +/// ``` +impl From for OnlinePacket { + fn from(packet: Packet) -> Self { + match packet.payload { + Payload::Online(x) => x, + _ => panic!("This is not an online packet!"), + } + } +} + +/// This allows implemenation for: +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::offline::OfflinePacket; +/// let offline: OfflinePacket = Packet::compose(source, position)?; +/// ``` +impl From for OfflinePacket { + fn from(packet: Packet) -> Self { + match packet.payload { + Payload::Offline(x) => x, + _ => panic!("This is not an online packet!"), + } + } +} + +/// This implementation allows for conversion of a OnlinePacket to a payload. +/// This isn't really used externally, but rather internally within the `Packet` struct. +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::online::OnlinePacket; +/// let payload: Payload = OnlinePacket(_).into(); +/// ``` +impl From for Payload { + fn from(packet: OnlinePacket) -> Self { + Payload::Online(packet) + } +} + +/// This implementation allows for conversion of a OfflinePacket to a payload. +/// This isn't really used externally, but rather internally within the `Packet` struct. +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::offline::OfflinePacket; +/// let payload: Payload = OfflinePacket(_).into(); +/// ``` +impl From for Payload { + fn from(packet: OfflinePacket) -> Self { + Payload::Offline(packet) + } +} + +/// A utility macro to add the `PacketId` trait to a packet. +/// This allows easier decoding of that packet. +#[macro_export] +macro_rules! packet_id { + ($name: ident, $id: literal) => { + impl PacketId for $name { + fn id() -> u8 { + $id + } + } + }; +} + +/// A utility macro that adds the implementation for any `OnlinePacket(Pk)` where +/// `Pk` can be converted to `Packet`, `Payload`, `OnlinePacket` or `OfflinePacket` +/// and vice versa. +/// +/// For example, we want unconnected pong to be unwrapped, we can do this without +/// a match statement like this: +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::online::OnlinePacket; +/// use raknet::packet::online::UnconnectedPing; +/// let some_packet = Packet::compose(&source, &mut position)?; +/// let connected_ping: UnconnectedPing = some_packet.into(); +/// ``` +/// +/// This macro also allows for converting any `OnlinePacket(Pk)` to a `Packet`, where `Pk` can +/// be directly converted into a packet. For example: +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::online::OnlinePacket; +/// use raknet::packet::online::UnconnectedPong; +/// +/// let packet: Packet = UnconnectedPong { +/// magic: Magic::new(), +/// timestamp: SystemTime::now(), +/// client_id: -129 +/// }.into(); +/// ``` +#[macro_export] +macro_rules! register_packets { + ($name: ident is $kind: ident, $($packet: ident),*) => { + $( + impl From<$packet> for $kind { + fn from(packet: $packet) -> Self { + $kind::$packet(packet) + } + } + + impl From<$kind> for $packet { + fn from(packet: $kind) -> Self { + match packet { + $kind::$packet(packet) => packet, + _ => panic!("Invalid packet type") + } + } + } + + impl From<$packet> for Packet { + fn from(payload: $packet) -> Self { + Self { + id: payload.get_id(), + payload: Payload::$name(payload.into()), + } + } + } + + impl From<$packet> for Payload { + fn from(payload: $packet) -> Self { + Self::$name(payload.into()) + } + } + + impl From for $packet { + fn from(payload: Payload) -> Self { + match payload { + Payload::$name(v) => v.into(), + _ => panic!("Invalid payload type"), + } + } + } + )* + }; +} diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs new file mode 100644 index 0000000..cad9f15 --- /dev/null +++ b/src/protocol/packet/offline.rs @@ -0,0 +1,133 @@ +use std::io::Write; +use std::net::SocketAddr; + +use binary_utils::error::BinaryError; +use binary_utils::*; +use byteorder::WriteBytesExt; + +#[cfg(feature = "mcpe")] +pub use crate::protocol::mcpe::packet::UnconnectedPong; + +use super::Packet; +use super::PacketId; +use super::Payload; +use crate::protocol::Magic; +use crate::{packet_id, register_packets}; + +/// A enum that represents all offline packets. +#[derive(Clone, Debug)] +pub enum OfflinePacket { + UnconnectedPing(UnconnectedPing), + OpenConnectRequest(OpenConnectRequest), + OpenConnectReply(OpenConnectReply), + SessionInfoRequest(SessionInfoRequest), + SessionInfoReply(SessionInfoReply), + #[cfg(feature = "mcpe")] + UnconnectedPong(UnconnectedPong), + #[cfg(not(feature = "mcpe"))] + UnconnectedPong(UnconnectedPong), + IncompatibleProtocolVersion(IncompatibleProtocolVersion), +} + +register_packets![ + Offline is OfflinePacket, + UnconnectedPing, + UnconnectedPong, + OpenConnectRequest, + OpenConnectReply, + SessionInfoRequest, + SessionInfoReply, + IncompatibleProtocolVersion +]; + +/// Unconnected Ping +#[derive(Debug, Clone, BinaryStream)] +pub struct UnconnectedPing { + pub timestamp: u64, + pub magic: Magic, + pub client_id: i64, +} +packet_id!(UnconnectedPing, 0x01); + +/// Unconnected Pong +#[cfg(not(feature = "mcpe"))] +#[derive(Debug, Clone, BinaryStream)] +pub struct UnconnectedPong { + pub timestamp: u64, + pub server_id: u64, + pub magic: Magic, +} +#[cfg(not(feature = "mcpe"))] +packet_id!(UnconnectedPong, 0x1c); + +/// This packet is the equivelant of the `OpenConnectRequest` packet in RakNet. +#[derive(Debug, Clone)] +pub struct OpenConnectRequest { + pub magic: Magic, + pub protocol: u8, + pub mtu_size: u16, +} +impl Streamable for OpenConnectRequest { + fn compose(source: &[u8], position: &mut usize) -> Result { + Ok(Self { + magic: Magic::compose(source, position)?, + protocol: u8::compose(source, position)?, + mtu_size: (source.len() + 1 + 28) as u16, + }) + } + + fn parse(&self) -> Result, BinaryError> { + let mut stream = Vec::::new(); + stream + .write(&self.magic.parse()?[..]) + .expect("Failed to parse open connect request"); + stream.write_u8(self.protocol)?; + // padding + for _ in 0..self.mtu_size { + stream.write_u8(0)?; + } + Ok(stream) + } +} +packet_id!(OpenConnectRequest, 0x05); + +// Open Connection Reply +/// Sent to the client when the server accepts a client. +/// This packet is the equivalent of the `Open Connect Reply 1` packet. +#[derive(Debug, Clone, BinaryStream)] +pub struct OpenConnectReply { + pub magic: Magic, + pub server_id: u64, + pub security: bool, + pub mtu_size: u16, +} +packet_id!(OpenConnectReply, 0x06); + +/// Session info, also known as Open Connect Request 2 +#[derive(Debug, Clone, BinaryStream)] +pub struct SessionInfoRequest { + pub magic: Magic, + pub address: SocketAddr, + pub mtu_size: u16, + pub client_id: i64, +} +packet_id!(SessionInfoRequest, 0x07); + +/// Session Info Reply, also known as Open Connect Reply 2 +#[derive(Debug, Clone, BinaryStream)] +pub struct SessionInfoReply { + pub magic: Magic, + pub server_id: u64, + pub client_address: SocketAddr, + pub mtu_size: u16, + pub security: bool, +} +packet_id!(SessionInfoReply, 0x08); + +#[derive(Debug, Clone, BinaryStream)] +pub struct IncompatibleProtocolVersion { + pub protocol: u8, + pub magic: Magic, + pub server_id: u64, +} +packet_id!(IncompatibleProtocolVersion, 0x19); \ No newline at end of file diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs new file mode 100644 index 0000000..d645932 --- /dev/null +++ b/src/protocol/packet/online.rs @@ -0,0 +1,134 @@ +use std::io::Write; +use std::net::IpAddr; +use std::net::Ipv4Addr; +use std::net::SocketAddr; + +use binary_utils::error::BinaryError; +use binary_utils::*; +use byteorder::BigEndian; +use byteorder::WriteBytesExt; + +use super::Packet; +use super::PacketId; +use super::Payload; +use crate::{packet_id, register_packets}; + +/// A enum that represents all online packets. +#[derive(Clone, Debug)] +pub enum OnlinePacket { + ConnectedPing(ConnectedPing), + ConnectedPong(ConnectedPong), + LostConnection(LostConnection), + ConnectionRequest(ConnectionRequest), + ConnectionAccept(ConnectionAccept), + NewConnection(NewConnection), + Disconnect(Disconnect), +} + +register_packets![ + Online is OnlinePacket, + ConnectedPing, + ConnectedPong, + ConnectionRequest, + ConnectionAccept, + NewConnection, + Disconnect +]; + +/// Connected Ping Packet +/// This packet is sent by the client to the server. +/// The server should respond with a `ConnectedPong` packet. +#[derive(Clone, Debug, BinaryStream)] +pub struct ConnectedPing { + pub time: i64, +} +packet_id!(ConnectedPing, 0x00); + +/// Connected Pong Packet +/// This packet is sent by the server to the client in response to a `ConnectedPing` packet. +#[derive(Clone, Debug, BinaryStream)] +pub struct ConnectedPong { + pub ping_time: i64, + pub pong_time: i64, +} +packet_id!(ConnectedPong, 0x03); + +/// A connection Request Request, this contains information about the client. Like it's +/// current time and the client id. +#[derive(Clone, Debug, BinaryStream)] +pub struct ConnectionRequest { + pub client_id: i64, + pub time: i64, +} +packet_id!(ConnectionRequest, 0x09); + +/// A connection Accept packet, this is sent by the server to the client. +/// This is sent by the server and contains information about the server. +#[derive(Clone, Debug)] +pub struct ConnectionAccept { + /// The address of the client connecting (locally?). + pub client_address: SocketAddr, + /// The system index is the index of the system that the client is connected to. + /// This is the index of the server on the client. + /// (Not sure why this is useful) + pub system_index: i16, + /// The internal id's of the server or alternative IP's of the server. + /// These are addresses the client will use if it can't connect to the server. + /// (Not sure why this is useful) + pub internal_id: SocketAddr, + /// The time of the timestamp the client sent with `ConnectionRequest`. + pub request_time: i64, + /// The time on the server. + pub timestamp: i64, +} + +impl Streamable for ConnectionAccept { + fn parse(&self) -> Result, BinaryError> { + let mut stream = Vec::new(); + stream.write_all(&self.client_address.parse()?[..])?; + stream.write_i16::(self.system_index)?; + for _ in 0..10 { + stream.write_all(&self.internal_id.parse()?[..])?; + } + stream.write_i64::(self.request_time)?; + stream.write_i64::(self.timestamp)?; + Ok(stream) + } + + fn compose(_source: &[u8], _position: &mut usize) -> Result { + Ok(Self { + client_address: SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120), + system_index: 0, + internal_id: SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), 1920), + request_time: 0, + timestamp: 0, + }) + } +} +packet_id!(ConnectionAccept, 0x10); + +/// Going to be completely Honest here, I have no idea what this is used for right now, +/// even after reading the source code. +#[derive(Clone, Debug, BinaryStream)] +pub struct NewConnection { + /// The external IP Address of the server. + pub server_address: SocketAddr, + /// The internal IP Address of the server. + pub system_address: SocketAddr, + /// The time of the timestamp the client sent with `ConnectionRequest`. + pub request_time: i64, + /// The time on the server. + pub timestamp: i64, +} +packet_id!(NewConnection, 0x13); + +/// A disconnect notification. Tells the client to disconnect. +#[derive(Clone, Debug, BinaryStream)] +pub struct Disconnect {} +packet_id!(Disconnect, 0x15); + +/// A connection lost notification. +/// This is sent by the client when it loses connection to the server. +#[derive(Clone, Debug, BinaryStream)] +pub struct LostConnection {} +packet_id!(LostConnection, 0x04); diff --git a/src/protocol/reliability.rs b/src/protocol/reliability.rs new file mode 100644 index 0000000..c548cb9 --- /dev/null +++ b/src/protocol/reliability.rs @@ -0,0 +1,95 @@ +#[derive(Clone, Debug, Copy)] +#[repr(u8)] +pub enum Reliability { + /// Unreliable (with no ack) + Unreliable = 0, + /// Unreliable with a sequence + UnreliableSeq, + /// Reliable + Reliable, + ReliableOrd, + /// Reliably sequenced **AND** ordered + ReliableSeq, + UnreliableAck, + ReliableAck, + ReliableOrdAck, +} + +impl Reliability { + pub fn from_flags(flags: u8) -> Self { + match (flags & 224) >> 5 { + 0 => Reliability::Unreliable, + 1 => Reliability::UnreliableSeq, + 2 => Reliability::Reliable, + 3 => Reliability::ReliableOrd, + 4 => Reliability::ReliableSeq, + 5 => Reliability::UnreliableAck, + 6 => Reliability::ReliableAck, + 7 => Reliability::ReliableOrdAck, + // we shouldn't error, but we'll just return unreliable + _ => Reliability::Unreliable, + } + } + + pub fn to_flags(&self) -> u8 { + match self { + Reliability::Unreliable => 0 << 5, + Reliability::UnreliableSeq => 1 << 5, + Reliability::Reliable => 2 << 5, + Reliability::ReliableOrd => 3 << 5, + Reliability::ReliableSeq => 4 << 5, + Reliability::UnreliableAck => 5 << 5, + Reliability::ReliableAck => 6 << 5, + Reliability::ReliableOrdAck => 7 << 5, + } + } + + /// Whether or not the packet is ordered. + pub fn is_ordered(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => true, + _ => false, + } + } + + /// Whether or not the packet is reliable. + pub fn is_reliable(&self) -> bool { + match self { + Self::Reliable | Self::ReliableOrd | Self::ReliableSeq | Self::ReliableOrdAck => true, + _ => false, + } + } + + /// Whether or not the packet is unreliable. + pub fn is_unreliable(&self) -> bool { + match self { + Self::Unreliable | Self::UnreliableSeq | Self::UnreliableAck => true, + _ => false, + } + } + + /// Whether or not the packet is sequenced. + pub fn is_sequenced(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableSeq => true, + _ => false, + } + } + + pub fn is_sequenced_or_ordered(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => { + true + } + _ => false, + } + } + + /// Whether or not the packet has an acknowledgement. + pub fn is_ack(&self) -> bool { + match self { + Self::UnreliableAck | Self::ReliableAck | Self::ReliableOrdAck => true, + _ => false, + } + } +} From fe3c63ccf686bddeb3c4f6cfb28c64eaf20bc6dd Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 30 Oct 2022 21:55:12 -0500 Subject: [PATCH 04/75] fmt: cargo fmt --- src/conn/mod.rs | 9 ++++++--- src/conn/queue.rs | 4 +--- src/conn/state.rs | 2 +- src/lib.rs | 2 +- src/protocol/mod.rs | 4 ++-- src/protocol/packet/offline.rs | 2 +- src/server/motd.rs | 3 --- 7 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 src/server/motd.rs diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 81f3810..0a47175 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -1,11 +1,14 @@ pub mod queue; pub mod state; -use std::{net::SocketAddr, sync::{Arc, Mutex}}; +use std::{ + net::SocketAddr, + sync::{Arc, Mutex}, +}; use tokio::sync::RwLock; -use self::queue::{SendQueue, RecvQueue}; +use self::queue::{RecvQueue, SendQueue}; /// This struct is utilized internally and represented /// as per each "connection" or "socket" to the server. @@ -23,5 +26,5 @@ pub struct Conn { /// This is only used internally. pub(crate) recv_queue: Arc>, - pub(crate) state: state::ConnState + pub(crate) state: state::ConnState, } diff --git a/src/conn/queue.rs b/src/conn/queue.rs index 4f1d57c..f377a65 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -144,6 +144,4 @@ pub struct SendQueue { } #[derive(Debug, Clone)] -pub struct RecvQueue { - -} \ No newline at end of file +pub struct RecvQueue {} diff --git a/src/conn/state.rs b/src/conn/state.rs index 1d7cb33..7668647 100644 --- a/src/conn/state.rs +++ b/src/conn/state.rs @@ -90,4 +90,4 @@ impl std::fmt::Display for ConnState { Self::Offline => write!(f, "Offline"), } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 28f2360..036c83a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ pub mod conn; +pub mod protocol; pub mod server; pub mod util; -pub mod protocol; \ No newline at end of file diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 63078c8..4ce206e 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod frame; -pub(crate) mod packet; pub(crate) mod magic; pub(crate) mod mcpe; +pub(crate) mod packet; pub mod reliability; -pub use magic::*; \ No newline at end of file +pub use magic::*; diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index cad9f15..677cd1f 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -130,4 +130,4 @@ pub struct IncompatibleProtocolVersion { pub magic: Magic, pub server_id: u64, } -packet_id!(IncompatibleProtocolVersion, 0x19); \ No newline at end of file +packet_id!(IncompatibleProtocolVersion, 0x19); diff --git a/src/server/motd.rs b/src/server/motd.rs deleted file mode 100644 index 1852e30..0000000 --- a/src/server/motd.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct Motd { - -} \ No newline at end of file From 1700f370ac81af6af04de0f53dddca0e71856e5d Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 12 Nov 2022 21:30:19 -0600 Subject: [PATCH 05/75] feat: Start working on recovery queues --- src/conn/queue.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/src/conn/queue.rs b/src/conn/queue.rs index f377a65..371f048 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -1,4 +1,103 @@ use std::collections::HashMap; +use std::collections::BTreeMap; +use std::collections::VecDeque; + +/// A specialized struct that will keep records of `T` +/// up to a certain capacity specified with +/// `RecoveryQueue::with_capacity(u32)` +/// during construction. +/// +/// By default the recovery queue +/// will store `255` records of `T`. +/// +/// The maximum records allowed are `u32::MAX`, however not +/// advised. +/// +/// ```rust +/// use rakrs::conn::queue::RecoveryQueue; +/// +/// // Create a new recovery queue, of u8 +/// let mut queue = RecoveryQueue::::new(); +/// let indexes = ( +/// // 0 +/// queue.insert(1), +/// // 1 +/// queue.insert(4), +/// // 2 +/// queue.insert(6) +/// ); +/// +/// queue.recover(1); // Result<0> +/// queue.recover(2); // Result<6> +/// queue.get(1); // Result<4> +/// +/// assert_eq!(queue.recover(1), Ok(4)); +/// assert_eq!(queue.get(1), Ok(4)); +/// assert_eq!(queue.get(4), Err()); +/// ``` +#[derive(Debug, Clone)] +pub struct RecoveryQueue { + recovery: VecDeque<(u32, Item)>, + capacity: u32, + index: u32 +} + +impl RecoveryQueue { + pub fn new() -> Self { + Self { + recovery: VecDeque::with_capacity(255), + capacity: 255, + index: 0 + } + } + + pub fn with_capacity(capacity: u32) -> Self { + Self { + recovery: VecDeque::with_capacity(capacity.try_into().unwrap()), + capacity, + index: 0 + } + } + + /// Add a new item into the recovery queue. + /// If the item addition exceeds the current + /// capacity of the queue, the queue is shifted. + /// + /// This method does not validate for duplicates, + /// for that, use `new_insert` + pub fn insert(&mut self, item: Item) -> u32 { + self.validate_capacity(); + + let idx = self.index; + self.recovery.push_back((idx, item)); + self.index += 1; + + return idx; + } + + /// Validates that adding a new entry will not exceed + /// the capacity of the queue itself. + fn validate_capacity(&mut self) { + if self.recovery.len() == self.capacity as usize { + // We have met the capacity of the queue pop the front + self.recovery.pop_front(); + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum RecoveryQueueError { + /// The index given is not valid, either because it is from the + /// future, or overflows. + Invalid, + /// The index given is not recoverable, but was cached earlier, + /// you should not try to retrieve this index again. + IndexOld, + /// The insertion failed because the Item is already recoverable. + /// + /// **This is only enforced if used with `insert_new`** + Duplicate +} /// An ordered queue is used to Index incoming packets over a channel /// within a reliable window time. @@ -27,7 +126,7 @@ use std::collections::HashMap; #[derive(Debug)] pub struct OrderedQueue { /// The queue of packets that are in order. Mapped to the time they were received. - queue: HashMap, + queue: BTreeMap, /// The current starting scope for the queue. /// A start scope or "window start" is the range of packets that we are currently allowing. /// Older packets will be ignored simply because they are old. @@ -52,7 +151,7 @@ where { pub fn new() -> Self { Self { - queue: HashMap::new(), + queue: BTreeMap::new(), scope: (0, 0), } } @@ -84,7 +183,7 @@ where self.clear_out_of_scope(); // now drain the queue - let mut map = HashMap::new(); + let mut map = BTreeMap::new(); std::mem::swap(&mut map, &mut self.queue); let mut clean = map.iter().collect::>(); @@ -141,6 +240,19 @@ pub struct SendQueue { /// a packet is sent reliably. We can resend these if they are /// Acked. send_seq: u32, + + /// The current index to use when sending a "reliable" packet. + /// This is incremented every time a packet is reliably sent + + /// This is a special queue nested within the send queue. It will + /// automatically clean up packets that "are out of scope" or + /// "outside the window" + ord_queue: OrderedQueue> + +} + +impl SendQueue { + } #[derive(Debug, Clone)] From 84c3d1ca7da9547955155a512e2e18f9f3898203 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 10 Dec 2022 21:51:40 -0600 Subject: [PATCH 06/75] feat(server): Server will now (sort of) handle connections (lol) --- src/conn/queue.rs | 93 +++++++++++++++++++++++++++++++----- src/lib.rs | 1 + src/server/mod.rs | 117 +++++++++++++++++++++++++++++++++++++++++++--- src/util/mod.rs | 17 +++++++ 4 files changed, 209 insertions(+), 19 deletions(-) diff --git a/src/conn/queue.rs b/src/conn/queue.rs index 371f048..90b4668 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -1,11 +1,43 @@ use std::collections::HashMap; -use std::collections::BTreeMap; use std::collections::VecDeque; +pub enum NetQueueError { + /// The insertion failed for any given reason. + InvalidInsertion, + /// The insertion failed and the reason is known. + InvalidInsertionKnown(String), + /// The `Item` failed to be removed from the queue. + ItemDeletionFail, + Other(E) +} + +pub trait NetQueue { + /// The `Item` of the queue. + // type Item = V; + + /// The "key" that each `Item` is stored under + /// (used for removal) + type KeyId; + + /// A custom error specifier for NetQueueError + type Error; + + /// Inserts `Item` into the queue, given the conditions are fulfilled. + fn insert(&mut self, item: Item) -> Result>; + + /// Remove an `Item` from the queue by providing an instance of `Self::KeyId` + fn remove(&mut self, key: Self::KeyId) -> Result>; + + /// Retrieves an `Item` from the queue, by reference. + fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError>; + + /// Clears the entire queue. + fn flush(&mut self) -> Result, NetQueueError>; +} + /// A specialized struct that will keep records of `T` -/// up to a certain capacity specified with -/// `RecoveryQueue::with_capacity(u32)` -/// during construction. +/// up to a certain capacity specified with `RecoveryQueue::with_capacity(u32)` +/// during construction. This means any records that are hold, are dropped off and forgotten. /// /// By default the recovery queue /// will store `255` records of `T`. @@ -37,9 +69,10 @@ use std::collections::VecDeque; /// ``` #[derive(Debug, Clone)] pub struct RecoveryQueue { + /// (index, resend_attempts, Item) recovery: VecDeque<(u32, Item)>, capacity: u32, - index: u32 + index: u32, } impl RecoveryQueue { @@ -59,6 +92,22 @@ impl RecoveryQueue { } } + /// Set the capacity of the recovery queue. + /// This may be called by raknet if a load of clients + /// start trying to connect or if the I/O of network + /// begins writing more than reading. + pub fn set_capacity(&mut self, factor: u32) -> bool { + // todo: IF factor is greator than self.capacity, replace + // todo: the current capacity with a vector of that capacity. + if factor > 0 { + self.capacity = factor; + self.validate_capacity(); + true + } else { + false + } + } + /// Add a new item into the recovery queue. /// If the item addition exceeds the current /// capacity of the queue, the queue is shifted. @@ -78,8 +127,7 @@ impl RecoveryQueue { /// Validates that adding a new entry will not exceed /// the capacity of the queue itself. fn validate_capacity(&mut self) { - if self.recovery.len() == self.capacity as usize { - // We have met the capacity of the queue pop the front + while self.recovery.len() >= self.capacity as usize { self.recovery.pop_front(); } } @@ -99,6 +147,22 @@ pub enum RecoveryQueueError { Duplicate } +/// A Record of `Item` where each `Item` may only live for `max_age`. +/// If an `Item` exceeds the duration of `max_age`, it is dropped. +/// +/// This structure is **NOT** ticked, meaning any records are stale +/// until the structure is interacted with. +#[derive(Clone, Debug)] +pub struct TimedRecoveryQueue { + /// The maximum age a packet is allowed to live in the queue for. + /// If the age is exceeded, the item will be dropped. + pub max_age: u32, + /// A private recovery queue, this will hold our (`Time`, Item) + /// We will then be able to clear out old packets by "Time", or if + /// the capacity of the queue is reached. + queue: RecoveryQueue<(u32, Item)> +} + /// An ordered queue is used to Index incoming packets over a channel /// within a reliable window time. /// @@ -126,7 +190,7 @@ pub enum RecoveryQueueError { #[derive(Debug)] pub struct OrderedQueue { /// The queue of packets that are in order. Mapped to the time they were received. - queue: BTreeMap, + queue: HashMap, /// The current starting scope for the queue. /// A start scope or "window start" is the range of packets that we are currently allowing. /// Older packets will be ignored simply because they are old. @@ -151,7 +215,7 @@ where { pub fn new() -> Self { Self { - queue: BTreeMap::new(), + queue: HashMap::new(), scope: (0, 0), } } @@ -183,7 +247,7 @@ where self.clear_out_of_scope(); // now drain the queue - let mut map = BTreeMap::new(); + let mut map = HashMap::new(); std::mem::swap(&mut map, &mut self.queue); let mut clean = map.iter().collect::>(); @@ -241,8 +305,13 @@ pub struct SendQueue { /// Acked. send_seq: u32, - /// The current index to use when sending a "reliable" packet. - /// This is incremented every time a packet is reliably sent + /// The reliable packet recovery queue. + /// This represents a "Sequence Index". Any socket can request + /// a sequence to resend via ack. These are saved here. + reliable_queue: TimedRecoveryQueue>, + + /// The unreliable packet sequence count. + unreliable_index: u32, /// This is a special queue nested within the send queue. It will /// automatically clean up packets that "are out of scope" or diff --git a/src/lib.rs b/src/lib.rs index 036c83a..4c697fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod conn; pub mod protocol; pub mod server; pub mod util; +pub mod error; \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index ae54cd9..a5fcf2b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,18 +1,121 @@ +use std::collections::HashMap; use std::{net::SocketAddr, sync::Arc}; +use binary_utils::Streamable; use tokio::net::UdpSocket; +use tokio::sync::Mutex; +use tokio::sync::mpsc::{Sender, Receiver, channel}; + +use crate::conn::Conn; +use crate::error::{self, server}; +use crate::error::server::ServerError; +use crate::protocol::mcpe::motd::Motd; +use crate::protocol::packet::Packet; +use crate::protocol::packet::offline::OfflinePacket; pub struct Listener { + /// If mcpe is true, this is the default MOTD, this is + /// the default MOTD to send to the client. You can change this later by setting + /// a motd in the `Conn` struct. + pub motd: Motd, + /// A server Id, passed in unconnected pong. + pub id: u64, + /// Whether or not the server is being served. + serving: bool, /// The current socket. sock: Option>, - /// Whether or not to use minecraft specific protocol. - /// This only effects the `Pong` packet where motd is sent. - mcpe: bool, - /// If mcpe is true, this is the default MOTD, this is - /// the default MOTD to send to the client. - motd: String, + + recv_comm: Receiver, + send_comm: Sender } impl Listener { - pub async fn bind(address: SocketAddr) {} + /// Binds a socket to the specified addres and starts listening. + /// EG: + /// ```rust + /// let mut server = Listener::bind("0.0.0.0:19132").await; + /// let (conn, stream) = server.accept().await?.unwrap(); + /// ``` + pub async fn bind(address: SocketAddr) -> Result { + let sock = match UdpSocket::bind(address).await { + Ok(s) => s, + Err(_) => { + return Err(ServerError::AddrBindErr) + } + }; + + let server_id: u64 = rand::random(); + let motd = Motd::new(server_id, format!("{}", address.port())); + + let (send_comm, recv_comm) = channel::(100); + + let listener = Self { + sock: Some(Arc::new(sock)), + id: server_id, + motd, + send_comm, + recv_comm, + serving: false, + // connections: Arc::new(Mutex::new(HashMap::new())) + }; + + return Ok(listener); + } + + /// Starts the listener! + /// ```rust + /// let mut server = Listener::bind("0.0.0.0:19132", true).await; + /// + /// // let's begin to listen to connections + /// server.start().await; + /// ``` + pub async fn start(&mut self) -> Result<(), ServerError> { + if self.serving { + return Err(ServerError::ServerRunning); + } + + let socket = self.sock.as_ref().unwrap().clone(); + + tokio::spawn(async move { + // We allocate here to prevent constant allocation of this array + let mut buf: [u8; 2048] = [0; 2048]; + + loop { + // The socket is not readable. We can not read it. + if socket.readable().await.is_err() { + continue; + } + + let length: usize; + let origin: SocketAddr; + + // We need to wait for either the socket to stop recieving, + // or a server close notification. + tokio::select! { + recv = socket.recv_from(&mut buf) => { + match recv { + Ok((l, o)) => { + length = l; + origin = o; + }, + Err(_) => continue + } + }, + // todo disconnect notification + }; + + // Do a quick status check to see if it's an offline packet + if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { + if packet.is_offline() { + // the packet is offline, we can check if it's a handshake packet by + } + } else { + // Not a valid raknet packet. + // Ignore the payload. + } + } + }); + + return Ok(()); + } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 62e125f..474e0c3 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,10 +1,13 @@ use std::{collections::HashMap, time::SystemTime}; +use std::net::{SocketAddr, ToSocketAddrs}; /// This is a fancy wrapper over a HashMap that serves as /// a time oriented cache, where you can optionally clean up /// old and un-used values. Key serves as a `packet_id` in /// rakrs, but this could be used else-where. /// +/// ## Deprecated in favor of `RecoveryQueue` +/// /// Usage example: /// ```rust /// use rakrs::util::CacheStore; @@ -64,3 +67,17 @@ where self.store.contains_key(key) } } + +pub fn to_address_token(remote: SocketAddr) -> String { + let mut address = remote.ip().to_string(); + address.push_str(":"); + address.push_str(remote.port().to_string().as_str()); + return address; +} + +pub fn from_address_token(remote: String) -> SocketAddr { + let mut parsed = remote + .to_socket_addrs() + .expect("Could not parse remote address."); + SocketAddr::from(parsed.next().unwrap()) +} \ No newline at end of file From f61341a63aa36aba04072355dd0122758164a341 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 10 Dec 2022 21:58:05 -0600 Subject: [PATCH 07/75] fmt: Run formatter --- src/conn/queue.rs | 17 +++++++---------- src/lib.rs | 2 +- src/server/mod.rs | 16 ++++++---------- src/util/mod.rs | 4 ++-- 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/conn/queue.rs b/src/conn/queue.rs index 90b4668..969977f 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -8,7 +8,7 @@ pub enum NetQueueError { InvalidInsertionKnown(String), /// The `Item` failed to be removed from the queue. ItemDeletionFail, - Other(E) + Other(E), } pub trait NetQueue { @@ -80,7 +80,7 @@ impl RecoveryQueue { Self { recovery: VecDeque::with_capacity(255), capacity: 255, - index: 0 + index: 0, } } @@ -88,7 +88,7 @@ impl RecoveryQueue { Self { recovery: VecDeque::with_capacity(capacity.try_into().unwrap()), capacity, - index: 0 + index: 0, } } @@ -144,7 +144,7 @@ pub enum RecoveryQueueError { /// The insertion failed because the Item is already recoverable. /// /// **This is only enforced if used with `insert_new`** - Duplicate + Duplicate, } /// A Record of `Item` where each `Item` may only live for `max_age`. @@ -160,7 +160,7 @@ pub struct TimedRecoveryQueue { /// A private recovery queue, this will hold our (`Time`, Item) /// We will then be able to clear out old packets by "Time", or if /// the capacity of the queue is reached. - queue: RecoveryQueue<(u32, Item)> + queue: RecoveryQueue<(u32, Item)>, } /// An ordered queue is used to Index incoming packets over a channel @@ -316,13 +316,10 @@ pub struct SendQueue { /// This is a special queue nested within the send queue. It will /// automatically clean up packets that "are out of scope" or /// "outside the window" - ord_queue: OrderedQueue> - + ord_queue: OrderedQueue>, } -impl SendQueue { - -} +impl SendQueue {} #[derive(Debug, Clone)] pub struct RecvQueue {} diff --git a/src/lib.rs b/src/lib.rs index 4c697fb..4f39506 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub mod conn; +pub mod error; pub mod protocol; pub mod server; pub mod util; -pub mod error; \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index a5fcf2b..6f7a84e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -3,15 +3,15 @@ use std::{net::SocketAddr, sync::Arc}; use binary_utils::Streamable; use tokio::net::UdpSocket; +use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::Mutex; -use tokio::sync::mpsc::{Sender, Receiver, channel}; use crate::conn::Conn; -use crate::error::{self, server}; use crate::error::server::ServerError; +use crate::error::{self, server}; use crate::protocol::mcpe::motd::Motd; -use crate::protocol::packet::Packet; use crate::protocol::packet::offline::OfflinePacket; +use crate::protocol::packet::Packet; pub struct Listener { /// If mcpe is true, this is the default MOTD, this is @@ -26,7 +26,7 @@ pub struct Listener { sock: Option>, recv_comm: Receiver, - send_comm: Sender + send_comm: Sender, } impl Listener { @@ -39,9 +39,7 @@ impl Listener { pub async fn bind(address: SocketAddr) -> Result { let sock = match UdpSocket::bind(address).await { Ok(s) => s, - Err(_) => { - return Err(ServerError::AddrBindErr) - } + Err(_) => return Err(ServerError::AddrBindErr), }; let server_id: u64 = rand::random(); @@ -106,9 +104,7 @@ impl Listener { // Do a quick status check to see if it's an offline packet if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { - if packet.is_offline() { - // the packet is offline, we can check if it's a handshake packet by - } + // get the socket meta_data from } else { // Not a valid raknet packet. // Ignore the payload. diff --git a/src/util/mod.rs b/src/util/mod.rs index 474e0c3..a50cb63 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,5 +1,5 @@ -use std::{collections::HashMap, time::SystemTime}; use std::net::{SocketAddr, ToSocketAddrs}; +use std::{collections::HashMap, time::SystemTime}; /// This is a fancy wrapper over a HashMap that serves as /// a time oriented cache, where you can optionally clean up @@ -80,4 +80,4 @@ pub fn from_address_token(remote: String) -> SocketAddr { .to_socket_addrs() .expect("Could not parse remote address."); SocketAddr::from(parsed.next().unwrap()) -} \ No newline at end of file +} From f83898e7c4cd1e6f6df754e6a4e9a438a556bdd1 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 10 Dec 2022 22:00:42 -0600 Subject: [PATCH 08/75] update(readme.md): Fix api --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index abff8bb..334d6de 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ A fully functional RakNet implementation in rust, asynchronously driven. - ```rust // Create a server use raknet::Listener; @@ -23,7 +22,7 @@ async fn my_handler(conn: RakConnection, mut stream: RakStream) { async fn main() { // Bind to a socket and allow minecraft protocol - let mut server = Listener::new("0.0.0.0:19132", true).await; + let mut server = Listener::bind("0.0.0.0:19132", true).await; server.motd = Motd::new( "Rust Bedrock Minecraft server", // 100 players, maximum From 980ba73c680c664a385df900f36fc57e841ce33e Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Dec 2022 17:31:55 -0600 Subject: [PATCH 09/75] feat: Unconnected pong --- src/conn/mod.rs | 9 +++ src/conn/state.rs | 2 +- src/error/client.rs | 1 + src/error/mod.rs | 2 + src/error/server.rs | 5 ++ src/protocol/packet/offline.rs | 2 +- src/server/event.rs | 15 +++++ src/server/mod.rs | 114 ++++++++++++++++++++++++++++++--- 8 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/error/client.rs create mode 100644 src/error/mod.rs create mode 100644 src/error/server.rs create mode 100644 src/server/event.rs diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 0a47175..d749604 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -10,6 +10,15 @@ use tokio::sync::RwLock; use self::queue::{RecvQueue, SendQueue}; +#[derive(Debug, Clone, Copy)] +pub struct ConnMeta { + /// This is important, and is stored within the server itself + /// This value is 0 until the connection state is `Connecting` + pub mtu_size: u16, + /// The state of the connection + pub state: state::ConnState +} + /// This struct is utilized internally and represented /// as per each "connection" or "socket" to the server. /// Each Connection has it's own Reference pointer to a diff --git a/src/conn/state.rs b/src/conn/state.rs index 7668647..d14c835 100644 --- a/src/conn/state.rs +++ b/src/conn/state.rs @@ -2,7 +2,7 @@ /// These are all possible states of a raknet session, and while accessible externally /// Please note that these are not states relied in the original implementation of /// raknet, which preserve both "Unconnected" and "Connected" -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub enum ConnState { /// The Session is not yet connected, but is actively trying to connect. /// Clients in this state are considered to be actively trying to connect. diff --git a/src/error/client.rs b/src/error/client.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/error/client.rs @@ -0,0 +1 @@ + diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..c07f47e --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1,2 @@ +pub mod client; +pub mod server; diff --git a/src/error/server.rs b/src/error/server.rs new file mode 100644 index 0000000..5cf0770 --- /dev/null +++ b/src/error/server.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone)] +pub enum ServerError { + AddrBindErr, + ServerRunning, +} diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 677cd1f..ce85bd6 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -6,7 +6,7 @@ use binary_utils::*; use byteorder::WriteBytesExt; #[cfg(feature = "mcpe")] -pub use crate::protocol::mcpe::packet::UnconnectedPong; +pub use crate::protocol::mcpe::UnconnectedPong; use super::Packet; use super::PacketId; diff --git a/src/server/event.rs b/src/server/event.rs new file mode 100644 index 0000000..3a29035 --- /dev/null +++ b/src/server/event.rs @@ -0,0 +1,15 @@ +use std::net::SocketAddr; + +use crate::protocol::mcpe::motd::Motd; + +#[derive(Debug, Clone)] +pub enum ServerEvent { + /// A request to refresh the MOTD, + /// the second value in this tuple represents + /// the `Motd` that will be used if the event is + /// disregarded. + RefreshMotdRequest(SocketAddr, Motd), + /// The response to a `RefreshMotdRequest`. + RefreshMotd(Motd), + +} \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 6f7a84e..c32b3a7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,17 +1,24 @@ +/// Server events module. Handles things like updating the MOTD +/// for certain connections. This is a notifier channel. +pub mod event; + use std::collections::HashMap; use std::{net::SocketAddr, sync::Arc}; use binary_utils::Streamable; use tokio::net::UdpSocket; -use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::{mpsc, oneshot}; use tokio::sync::Mutex; use crate::conn::Conn; use crate::error::server::ServerError; use crate::error::{self, server}; +use crate::protocol::Magic; use crate::protocol::mcpe::motd::Motd; -use crate::protocol::packet::offline::OfflinePacket; -use crate::protocol::packet::Packet; +use crate::protocol::packet::offline::{OfflinePacket, UnconnectedPong, IncompatibleProtocolVersion, OpenConnectReply}; +use crate::protocol::packet::{Packet, Payload}; + +use self::event::ServerEvent; pub struct Listener { /// If mcpe is true, this is the default MOTD, this is @@ -25,8 +32,10 @@ pub struct Listener { /// The current socket. sock: Option>, - recv_comm: Receiver, - send_comm: Sender, + recv_comm: mpsc::Receiver, + send_comm: mpsc::Sender, + recv_evnt: mpsc::Receiver<(ServerEvent, oneshot::Sender)>, + send_evnt: mpsc::Sender<(ServerEvent, oneshot::Sender)>, } impl Listener { @@ -45,7 +54,10 @@ impl Listener { let server_id: u64 = rand::random(); let motd = Motd::new(server_id, format!("{}", address.port())); - let (send_comm, recv_comm) = channel::(100); + // The buffer is 100 here because locking on large servers may cause + // locking to take longer, and this accounts for that. + let (send_comm, recv_comm) = mpsc::channel::(100); + let (send_evnt, recv_evnt) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); let listener = Self { sock: Some(Arc::new(sock)), @@ -53,6 +65,8 @@ impl Listener { motd, send_comm, recv_comm, + send_evnt, + recv_evnt, serving: false, // connections: Arc::new(Mutex::new(HashMap::new())) }; @@ -73,10 +87,15 @@ impl Listener { } let socket = self.sock.as_ref().unwrap().clone(); + let send_comm = self.send_comm.clone(); + let server_id = self.id.clone(); + let default_motd = self.motd.clone(); + let ev_s = self.send_evnt.clone(); tokio::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; + let motd_default = default_motd.clone(); loop { // The socket is not readable. We can not read it. @@ -102,9 +121,75 @@ impl Listener { // todo disconnect notification }; - // Do a quick status check to see if it's an offline packet + // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { - // get the socket meta_data from + // if this is an offline packet, we can retrieve the buffer we should send. + match packet.payload { + Payload::Offline(pk) => { + // Offline packets are not buffered to the user. + // The reason for this is because we don't wish for the user to be able to disrupt + // raknet protocol, and handshaking. + match pk { + OfflinePacket::UnconnectedPing(_) => { + let (resp_tx, resp_rx) = oneshot::channel::(); + let mut motd: Motd = motd_default.clone(); + + ev_s.send((ServerEvent::RefreshMotdRequest(origin, motd.clone()), resp_tx)).await.ok().unwrap(); + + if let Ok(res) = resp_rx.await { + // this was a motd event, + // lets send the pong! + + // get the motd from the server event otherwise use defaults. + match res { + ServerEvent::RefreshMotd(m) => motd = m, + _ => {} + }; + } + + // unconnected pong signature is different if MCPE is specified. + let resp = UnconnectedPong { + timestamp: raknet_start(), + server_id, + magic: Magic::new(), + #[cfg(feature = "mcpe")] + motd + }; + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + }, + OfflinePacket::OpenConnectRequest(pk) => { + // todo make a constant for this + if pk.protocol != 10_u8 { + let resp = IncompatibleProtocolVersion { + protocol: pk.protocol, + magic: Magic::new(), + server_id + }; + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + + let resp = OpenConnectReply { + server_id, + // todo allow encryption, find out if this is MCPE specific + security: false, + magic: Magic::new(), + mtu_size: pk.mtu_size + }; + send_packet_to_socket(&socket, resp.into(), origin).await; + }, + _ => { + // everything else shuold be sent to the socket + } + } + }, + Payload::Online(pk) => { + // online packets need to be handled, but not yet :o + } + } } else { // Not a valid raknet packet. // Ignore the payload. @@ -115,3 +200,16 @@ impl Listener { return Ok(()); } } + +async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: SocketAddr) { + if let Err(e) = socket.send_to(&mut packet.parse().unwrap()[..], origin).await { + // todo debug + } +} + +pub(crate) fn raknet_start() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64 +} \ No newline at end of file From 726b1c988c347f215d6bd3eebefc9eef81662f9a Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Dec 2022 21:19:04 -0600 Subject: [PATCH 10/75] feat: Add connection handshake -skip --- src/conn/mod.rs | 56 +++++++++++++++++-- src/server/event.rs | 11 +++- src/server/mod.rs | 128 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 165 insertions(+), 30 deletions(-) diff --git a/src/conn/mod.rs b/src/conn/mod.rs index d749604..c8300de 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -4,25 +4,51 @@ pub mod state; use std::{ net::SocketAddr, sync::{Arc, Mutex}, + time::SystemTime, }; -use tokio::sync::RwLock; +use tokio::{ + net::UdpSocket, + sync::{mpsc, oneshot, RwLock}, +}; + +use crate::server::{ + event::{ServerEvent, ServerEventResponse}, + raknet_start, +}; use self::queue::{RecvQueue, SendQueue}; +pub(crate) type ConnDispatcher = + mpsc::Receiver<(ServerEvent, oneshot::Sender)>; +pub(crate) type ConnEvtChan = Arc>; +pub(crate) type ConnNetChan = Arc>>>; #[derive(Debug, Clone, Copy)] pub struct ConnMeta { /// This is important, and is stored within the server itself /// This value is 0 until the connection state is `Connecting` pub mtu_size: u16, - /// The state of the connection - pub state: state::ConnState + /// The time this connection last sent any data. This will be used during server tick. + pub recv_time: u64, +} + +impl ConnMeta { + pub fn new(mtu_size: u16) -> Self { + Self { + mtu_size, + recv_time: raknet_start(), + } + } } /// This struct is utilized internally and represented /// as per each "connection" or "socket" to the server. -/// Each Connection has it's own Reference pointer to a -/// socket dedicated to this connection. +/// This is **NOT** a struct that supports connecting TO +/// a RakNet instance, but rather a struct that HOLDS a +/// inbound connection connecting to a `Listener` instance. +/// +/// Each Connection has it's own channel for recieving +/// buffers that come from this address. pub struct Conn { /// The address of the connection /// This is internally tokenized by rak-rs @@ -36,4 +62,24 @@ pub struct Conn { pub(crate) recv_queue: Arc>, pub(crate) state: state::ConnState, + + /// The network channel, this is where the connection will be recieving it's packets. + /// If the channel is dropped, the connection is expected to drop as well, this behavior + /// has not been implemented yet. + net: ConnNetChan, + /// The event IO to communicate with the listener. + /// This is responsible for refreshing the Motd and any other overhead like, + /// raknet voice channels or plugins, however these have not been implemented yet. + dispatch: ConnEvtChan, +} + +impl Conn { + /// Initializes a new Connection instance. + pub fn new( + address: SocketAddr, + socket: &Arc, + net: mpsc::Receiver>, + dispatch: ConnDispatcher, + ) -> Self { + } } diff --git a/src/server/event.rs b/src/server/event.rs index 3a29035..adc7c97 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -9,7 +9,14 @@ pub enum ServerEvent { /// the `Motd` that will be used if the event is /// disregarded. RefreshMotdRequest(SocketAddr, Motd), + /// Requests the client to update their mtu size. + /// This event is dispatched before the client fully connects + /// allowing you to control the MtuSize. + SetMtuSize(u16), +} + +#[derive(Debug, Clone)] +pub enum ServerEventResponse { /// The response to a `RefreshMotdRequest`. RefreshMotd(Motd), - -} \ No newline at end of file +} diff --git a/src/server/mod.rs b/src/server/mod.rs index c32b3a7..11f80f7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,19 +7,27 @@ use std::{net::SocketAddr, sync::Arc}; use binary_utils::Streamable; use tokio::net::UdpSocket; -use tokio::sync::{mpsc, oneshot}; use tokio::sync::Mutex; +use tokio::sync::{mpsc, oneshot}; -use crate::conn::Conn; +use crate::conn::{Conn, ConnMeta}; use crate::error::server::ServerError; use crate::error::{self, server}; -use crate::protocol::Magic; use crate::protocol::mcpe::motd::Motd; -use crate::protocol::packet::offline::{OfflinePacket, UnconnectedPong, IncompatibleProtocolVersion, OpenConnectReply}; +use crate::protocol::packet::offline::{ + IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, +}; use crate::protocol::packet::{Packet, Payload}; +use crate::protocol::Magic; +use crate::server::event::ServerEventResponse; use self::event::ServerEvent; +pub type Connection = ( + ConnMeta, + mpsc::Sender>, + mpsc::Sender<(ServerEvent, oneshot::Sender)>, +); pub struct Listener { /// If mcpe is true, this is the default MOTD, this is /// the default MOTD to send to the client. You can change this later by setting @@ -31,11 +39,14 @@ pub struct Listener { serving: bool, /// The current socket. sock: Option>, + /// A Hashmap off all current connections along with a sending channel + /// and some meta data like the time of connection, and the requested MTU_Size + connections: Arc>>, + /// The recieve communication channel, This is used to dispatch connections between a handle + /// It allows you to use the syntax sugar for `Listener::accept()`. recv_comm: mpsc::Receiver, send_comm: mpsc::Sender, - recv_evnt: mpsc::Receiver<(ServerEvent, oneshot::Sender)>, - send_evnt: mpsc::Sender<(ServerEvent, oneshot::Sender)>, } impl Listener { @@ -57,7 +68,8 @@ impl Listener { // The buffer is 100 here because locking on large servers may cause // locking to take longer, and this accounts for that. let (send_comm, recv_comm) = mpsc::channel::(100); - let (send_evnt, recv_evnt) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); + let (send_evnt, recv_evnt) = + mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); let listener = Self { sock: Some(Arc::new(sock)), @@ -65,10 +77,8 @@ impl Listener { motd, send_comm, recv_comm, - send_evnt, - recv_evnt, serving: false, - // connections: Arc::new(Mutex::new(HashMap::new())) + connections: Arc::new(Mutex::new(HashMap::new())), }; return Ok(listener); @@ -90,7 +100,7 @@ impl Listener { let send_comm = self.send_comm.clone(); let server_id = self.id.clone(); let default_motd = self.motd.clone(); - let ev_s = self.send_evnt.clone(); + let connections = self.connections.clone(); tokio::spawn(async move { // We allocate here to prevent constant allocation of this array @@ -123,6 +133,35 @@ impl Listener { // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { + // This is a valid packet, let's check if a session exists, if not, we should create it. + // Event if the connection is only in offline mode. + let mut sessions = connections.lock().await; + + if !sessions.contains_key(&origin) { + let meta = ConnMeta::new(0); + let (net_send, net_recv) = mpsc::channel::>(10); + let (evt_send, evt_recv) = mpsc::channel::<( + ServerEvent, + oneshot::Sender, + )>(10); + let connection = Conn::new(origin, &socket, net_recv, evt_recv); + + // Add the connection to the available connections list. + // we're using the name "sessions" here to differeniate + sessions.insert(origin, (meta, net_send, evt_send)); + + // notify the connection communicator + if let Err(err) = send_comm.send(connection).await { + // there was an error, and we should terminate this connection immediately. + sessions.remove(&origin); + continue; + } + } + + // We're dropping here because we don't know if we'll need it + // We don't want to hold the lock longer than we need to. + drop(sessions); + // if this is an offline packet, we can retrieve the buffer we should send. match packet.payload { Payload::Offline(pk) => { @@ -131,10 +170,22 @@ impl Listener { // raknet protocol, and handshaking. match pk { OfflinePacket::UnconnectedPing(_) => { - let (resp_tx, resp_rx) = oneshot::channel::(); + let (resp_tx, resp_rx) = + oneshot::channel::(); let mut motd: Motd = motd_default.clone(); + let sessions = connections.lock().await; - ev_s.send((ServerEvent::RefreshMotdRequest(origin, motd.clone()), resp_tx)).await.ok().unwrap(); + if let Err(err) = sessions[&origin] + .2 + .send(( + ServerEvent::RefreshMotdRequest(origin, motd.clone()), + resp_tx, + )) + .await + { + // todo event error, + // we're gonna ignore it and continue by sending default motd. + } if let Ok(res) = resp_rx.await { // this was a motd event, @@ -142,7 +193,7 @@ impl Listener { // get the motd from the server event otherwise use defaults. match res { - ServerEvent::RefreshMotd(m) => motd = m, + ServerEventResponse::RefreshMotd(m) => motd = m, _ => {} }; } @@ -153,19 +204,19 @@ impl Listener { server_id, magic: Magic::new(), #[cfg(feature = "mcpe")] - motd + motd, }; send_packet_to_socket(&socket, resp.into(), origin).await; continue; - }, + } OfflinePacket::OpenConnectRequest(pk) => { // todo make a constant for this if pk.protocol != 10_u8 { let resp = IncompatibleProtocolVersion { protocol: pk.protocol, magic: Magic::new(), - server_id + server_id, }; send_packet_to_socket(&socket, resp.into(), origin).await; @@ -177,15 +228,43 @@ impl Listener { // todo allow encryption, find out if this is MCPE specific security: false, magic: Magic::new(), - mtu_size: pk.mtu_size + // todo make this configurable, this is sent to the client to change + // it's mtu size, right now we're using what the client prefers. + // however in some cases this may not be the preferred use case, for instance + // on servers with larger worlds, you may want a larger mtu size + mtu_size: pk.mtu_size, }; send_packet_to_socket(&socket, resp.into(), origin).await; - }, + } + OfflinePacket::SessionInfoRequest(pk) => { + let mut sessions = connections.lock().await; + let resp = SessionInfoReply { + server_id, + client_address: origin, + magic: Magic::new(), + mtu_size: pk.mtu_size, + security: false, + }; + + // update the sessions mtuSize, this is referred to internally, we also will send this event to the client + // event channel. However we are not expecting a response. + drop( + sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size, + ); + + let (resp_tx, resp_rx) = + oneshot::channel::(); + sessions[&origin] + .2 + .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)); + + send_packet_to_socket(&socket, resp.into(), origin); + } _ => { - // everything else shuold be sent to the socket + // everything else should be sent to the socket } } - }, + } Payload::Online(pk) => { // online packets need to be handled, but not yet :o } @@ -202,7 +281,10 @@ impl Listener { } async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: SocketAddr) { - if let Err(e) = socket.send_to(&mut packet.parse().unwrap()[..], origin).await { + if let Err(e) = socket + .send_to(&mut packet.parse().unwrap()[..], origin) + .await + { // todo debug } } @@ -212,4 +294,4 @@ pub(crate) fn raknet_start() -> u64 { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() as u64 -} \ No newline at end of file +} From a4c1ebdab1a9d3b2c44ec0a62ac205ad8047e179 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Dec 2022 21:44:59 -0600 Subject: [PATCH 11/75] chore(README.md): Fix packaged name in example -skip --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 334d6de..7e5bbb3 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ A fully functional RakNet implementation in rust, asynchronously driven. ```rust // Create a server -use raknet::Listener; -use raknet::util::handle; -use raknet::util::mcpe; +use rakrs::Listener; +use rakrs::util::handle; +use rakrs::util::mcpe; async fn my_handler(conn: RakConnection, mut stream: RakStream) { // The `conn.recv()` method constructs a `Packet` from the stream From da51459c79a324e333bd342f673f8bba129ca3bd Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Mon, 12 Dec 2022 00:56:16 -0600 Subject: [PATCH 12/75] feat: Add ability to close server through sephamores and notifiers --- src/conn/mod.rs | 9 ++-- src/conn/state.rs | 6 +-- src/error/server.rs | 7 ++- src/server/event.rs | 10 ++++- src/server/mod.rs | 106 ++++++++++++++++++++++++++++++++++++-------- 5 files changed, 111 insertions(+), 27 deletions(-) diff --git a/src/conn/mod.rs b/src/conn/mod.rs index c8300de..9cb2b68 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -49,7 +49,7 @@ impl ConnMeta { /// /// Each Connection has it's own channel for recieving /// buffers that come from this address. -pub struct Conn { +pub struct Connection { /// The address of the connection /// This is internally tokenized by rak-rs pub address: SocketAddr, @@ -61,7 +61,7 @@ pub struct Conn { /// This is only used internally. pub(crate) recv_queue: Arc>, - pub(crate) state: state::ConnState, + pub(crate) state: state::ConnectionState, /// The network channel, this is where the connection will be recieving it's packets. /// If the channel is dropped, the connection is expected to drop as well, this behavior @@ -73,7 +73,7 @@ pub struct Conn { dispatch: ConnEvtChan, } -impl Conn { +impl Connection { /// Initializes a new Connection instance. pub fn new( address: SocketAddr, @@ -82,4 +82,7 @@ impl Conn { dispatch: ConnDispatcher, ) -> Self { } + + /// Initializes the client tick. + pub async fn tick() } diff --git a/src/conn/state.rs b/src/conn/state.rs index d14c835..0139db0 100644 --- a/src/conn/state.rs +++ b/src/conn/state.rs @@ -3,7 +3,7 @@ /// Please note that these are not states relied in the original implementation of /// raknet, which preserve both "Unconnected" and "Connected" #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -pub enum ConnState { +pub enum ConnectionState { /// The Session is not yet connected, but is actively trying to connect. /// Clients in this state are considered to be actively trying to connect. Connecting, @@ -40,7 +40,7 @@ pub enum ConnState { Offline, } -impl ConnState { +impl ConnectionState { /// Returns whether or not the Session is reliable. /// Reliable sessions are sessions that are not: /// - Offline @@ -78,7 +78,7 @@ impl ConnState { } } -impl std::fmt::Display for ConnState { +impl std::fmt::Display for ConnectionState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Connecting => write!(f, "Connecting"), diff --git a/src/error/server.rs b/src/error/server.rs index 5cf0770..36ea6d3 100644 --- a/src/error/server.rs +++ b/src/error/server.rs @@ -1,5 +1,8 @@ #[derive(Debug, Clone)] pub enum ServerError { AddrBindErr, - ServerRunning, -} + AlreadyOnline, + NotListening, + Killed, + Reset +} \ No newline at end of file diff --git a/src/server/event.rs b/src/server/event.rs index adc7c97..acc5313 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -1,6 +1,6 @@ use std::net::SocketAddr; -use crate::protocol::mcpe::motd::Motd; +use crate::{protocol::mcpe::motd::Motd, conn::state::ConnectionState}; #[derive(Debug, Clone)] pub enum ServerEvent { @@ -13,6 +13,14 @@ pub enum ServerEvent { /// This event is dispatched before the client fully connects /// allowing you to control the MtuSize. SetMtuSize(u16), + /// Disconnect the client immediately + /// Sent to the client to immediately disconnect the client. + /// If you ignore this, the connection will be dropped automatically + /// however this event is fired to allow graceful handling of a disconnect + DisconnectImmediately, + /// A request from the listener to update a connection's state. + /// This is done during handshake or if the connection is timed out. + UpdateConnectionState(ConnectionState) } #[derive(Debug, Clone)] diff --git a/src/server/mod.rs b/src/server/mod.rs index 11f80f7..d425fc2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,23 +7,23 @@ use std::{net::SocketAddr, sync::Arc}; use binary_utils::Streamable; use tokio::net::UdpSocket; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, Semaphore, Notify}; use tokio::sync::{mpsc, oneshot}; -use crate::conn::{Conn, ConnMeta}; +use crate::conn::{Connection, ConnMeta}; use crate::error::server::ServerError; -use crate::error::{self, server}; use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, }; +use crate::protocol::packet::online::Disconnect; use crate::protocol::packet::{Packet, Payload}; use crate::protocol::Magic; use crate::server::event::ServerEventResponse; use self::event::ServerEvent; -pub type Connection = ( +pub type Session = ( ConnMeta, mpsc::Sender>, mpsc::Sender<(ServerEvent, oneshot::Sender)>, @@ -41,12 +41,17 @@ pub struct Listener { sock: Option>, /// A Hashmap off all current connections along with a sending channel /// and some meta data like the time of connection, and the requested MTU_Size - connections: Arc>>, - + connections: Arc>>, /// The recieve communication channel, This is used to dispatch connections between a handle /// It allows you to use the syntax sugar for `Listener::accept()`. - recv_comm: mpsc::Receiver, - send_comm: mpsc::Sender, + recv_comm: mpsc::Receiver, + send_comm: mpsc::Sender, + /// A Notifier (sephamore) that will wait until all notified listeners + /// are completed, and finish closing. + closer: Arc, + /// This is a notifier that acknowledges all connections have been removed from the server successfully. + /// This is important to prevent memory leaks if the process is continously running. + cleanup: Arc } impl Listener { @@ -67,7 +72,7 @@ impl Listener { // The buffer is 100 here because locking on large servers may cause // locking to take longer, and this accounts for that. - let (send_comm, recv_comm) = mpsc::channel::(100); + let (send_comm, recv_comm) = mpsc::channel::(100); let (send_evnt, recv_evnt) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); @@ -79,6 +84,8 @@ impl Listener { recv_comm, serving: false, connections: Arc::new(Mutex::new(HashMap::new())), + closer: Arc::new(Semaphore::new(0)), + cleanup: Arc::new(Notify::new()) }; return Ok(listener); @@ -93,7 +100,7 @@ impl Listener { /// ``` pub async fn start(&mut self) -> Result<(), ServerError> { if self.serving { - return Err(ServerError::ServerRunning); + return Err(ServerError::AlreadyOnline); } let socket = self.sock.as_ref().unwrap().clone(); @@ -101,6 +108,8 @@ impl Listener { let server_id = self.id.clone(); let default_motd = self.motd.clone(); let connections = self.connections.clone(); + let closer = self.closer.clone(); + let cleanup = self.cleanup.clone(); tokio::spawn(async move { // We allocate here to prevent constant allocation of this array @@ -128,6 +137,26 @@ impl Listener { Err(_) => continue } }, + _ = closer.acquire() => { + // we got a close notification, disconnect all clients + let mut sessions = connections.lock().await; + for conn in sessions.drain() { + // send a disconnect notification to each connection + let disconnect = Disconnect {}; + send_packet_to_socket(&socket, disconnect.into(), conn.0).await; + + // absolutely ensure disconnection + let (rx, rs) = oneshot::channel::(); + if let Err(_) = conn.1.2.send((ServerEvent::DisconnectImmediately, rx)).await { + // failed to send the connection, we're gonna drop it either way + } + drop(conn); + } + + cleanup.notify_one(); + + break; + } // todo disconnect notification }; @@ -144,7 +173,7 @@ impl Listener { ServerEvent, oneshot::Sender, )>(10); - let connection = Conn::new(origin, &socket, net_recv, evt_recv); + let connection = Connection::new(origin, &socket, net_recv, evt_recv); // Add the connection to the available connections list. // we're using the name "sessions" here to differeniate @@ -235,6 +264,7 @@ impl Listener { mtu_size: pk.mtu_size, }; send_packet_to_socket(&socket, resp.into(), origin).await; + continue; } OfflinePacket::SessionInfoRequest(pk) => { let mut sessions = connections.lock().await; @@ -259,25 +289,65 @@ impl Listener { .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)); send_packet_to_socket(&socket, resp.into(), origin); + continue; } _ => { // everything else should be sent to the socket } } - } - Payload::Online(pk) => { - // online packets need to be handled, but not yet :o - } + }, + _ => {} + }; + } + + // Packet may be valid, but we'll let the connection decide this + let sessions = connections.lock().await; + if sessions.contains_key(&origin) { + if let Err(err) = sessions[&origin].1.send(buf[..length].to_vec()).await { + // todo log error! } - } else { - // Not a valid raknet packet. - // Ignore the payload. } } }); return Ok(()); } + + /// Must be called in after both `Listener::bind` AND `Listener::start`. This function + /// is used to recieve and accept connections. Alternatively, you can refuse a connection + /// by dropping it when you accept it. + pub async fn accept(&mut self) -> Result { + if !self.serving { + Err(ServerError::NotListening) + } else { + tokio::select! { + receiver = self.recv_comm.recv() => { + match receiver { + Some(c) => Ok(c), + None => Err(ServerError::Killed) + } + }, + _ = self.closer.acquire() => { + Err(ServerError::Killed) + } + } + } + } + + /// Stops the listener and frees the socket. + pub async fn stop(&mut self) -> Result<(), ServerError> { + if self.closer.is_closed() { + return Ok(()); + } + + self.closer.close(); + self.cleanup.notified().await; + + self.sock = None; + self.serving = false; + + Ok(()) + } } async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: SocketAddr) { From bbec344c3dbf004f54e551fbdd30c1d18c2806fa Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Mon, 12 Dec 2022 00:56:51 -0600 Subject: [PATCH 13/75] chore: Format proj --- src/conn/mod.rs | 2 +- src/error/server.rs | 4 ++-- src/server/event.rs | 4 ++-- src/server/mod.rs | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 9cb2b68..c8f517a 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -84,5 +84,5 @@ impl Connection { } /// Initializes the client tick. - pub async fn tick() + pub async fn tick() {} } diff --git a/src/error/server.rs b/src/error/server.rs index 36ea6d3..636d761 100644 --- a/src/error/server.rs +++ b/src/error/server.rs @@ -4,5 +4,5 @@ pub enum ServerError { AlreadyOnline, NotListening, Killed, - Reset -} \ No newline at end of file + Reset, +} diff --git a/src/server/event.rs b/src/server/event.rs index acc5313..6a4ee7e 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -1,6 +1,6 @@ use std::net::SocketAddr; -use crate::{protocol::mcpe::motd::Motd, conn::state::ConnectionState}; +use crate::{conn::state::ConnectionState, protocol::mcpe::motd::Motd}; #[derive(Debug, Clone)] pub enum ServerEvent { @@ -20,7 +20,7 @@ pub enum ServerEvent { DisconnectImmediately, /// A request from the listener to update a connection's state. /// This is done during handshake or if the connection is timed out. - UpdateConnectionState(ConnectionState) + UpdateConnectionState(ConnectionState), } #[derive(Debug, Clone)] diff --git a/src/server/mod.rs b/src/server/mod.rs index d425fc2..a0d9d03 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,10 +7,10 @@ use std::{net::SocketAddr, sync::Arc}; use binary_utils::Streamable; use tokio::net::UdpSocket; -use tokio::sync::{Mutex, Semaphore, Notify}; use tokio::sync::{mpsc, oneshot}; +use tokio::sync::{Mutex, Notify, Semaphore}; -use crate::conn::{Connection, ConnMeta}; +use crate::conn::{ConnMeta, Connection}; use crate::error::server::ServerError; use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ @@ -51,7 +51,7 @@ pub struct Listener { closer: Arc, /// This is a notifier that acknowledges all connections have been removed from the server successfully. /// This is important to prevent memory leaks if the process is continously running. - cleanup: Arc + cleanup: Arc, } impl Listener { @@ -85,7 +85,7 @@ impl Listener { serving: false, connections: Arc::new(Mutex::new(HashMap::new())), closer: Arc::new(Semaphore::new(0)), - cleanup: Arc::new(Notify::new()) + cleanup: Arc::new(Notify::new()), }; return Ok(listener); @@ -295,7 +295,7 @@ impl Listener { // everything else should be sent to the socket } } - }, + } _ => {} }; } From 8fac4071d28a10dbe2302937345c853d2f9dddce Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 24 Dec 2022 23:33:29 -0600 Subject: [PATCH 14/75] chore: Add some debug logging + some additions to server & conn --- LICENSE.md | 352 +++++++++++++------------ resources/img/Connection Handshake.png | Bin 0 -> 196981 bytes resources/mddoc/Raknet.md | 0 resources/mddoc/protocol/README.md | 0 resources/mddoc/reliability.md | 0 src/conn/mod.rs | 7 +- src/conn/queue.rs | 47 +++- src/protocol/frame.rs | 30 ++- src/server/mod.rs | 99 +++++-- src/util/debug.rs | 15 ++ src/util/mod.rs | 37 +++ 11 files changed, 390 insertions(+), 197 deletions(-) create mode 100644 resources/img/Connection Handshake.png create mode 100644 resources/mddoc/Raknet.md create mode 100644 resources/mddoc/protocol/README.md create mode 100644 resources/mddoc/reliability.md create mode 100644 src/util/debug.rs diff --git a/LICENSE.md b/LICENSE.md index 73d813f..7c86c2f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,185 +4,189 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS - + Copyright 2021 Suruloon Studios - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - + + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/resources/img/Connection Handshake.png b/resources/img/Connection Handshake.png new file mode 100644 index 0000000000000000000000000000000000000000..5cdaf2fc1705f9c3d246492e9196cec4570395d1 GIT binary patch literal 196981 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

LFkK}Sb# z+=&He+(pju=0{Z}KMO;l#r(km)L+^+4pyDB=muycgPAYijBkusZL&z3-0xyO=H{;^ ztXABqDUm-@ekt$b_?9q1C?%q*xrYE?YZ}O`HATI7AxH_YNtX}tMES~DFNJRT+i7G8 z?NGiJ(Wy5H4LVXCAh>vvs~>>I3|vs_qdI2fTZbG~IYg?YP!;wTt6k2Bw7|))8L0Mt zXbYRg92KD7UvO;gGgf&A)Z=z2CprTLAyxR5OBoF*hg?KQKX-ZG zeBQXfPGJ#znufx=;VfKzxE-<8_jVvLYvv8V{0m^s!svi@smOUUX%ko~NivH*W;H;M7ui1zIj+JuAA`VsZzWs2ic-e8y2M z=&Er8S%vikn^$BN6S>M@)gK-H6XyRz1%TRsVicT0dn3;|Msg!T9eO{fDdn19AkcX< zPMAe^i(Y(-RdT`}eyPlC*#=8lGl)xfKF)f>rb^ksi`E`FBrjECZK?z5c(p|2oj)X9 zvDd!qV20;2Nfa77(4qoxEZUvd%C#^rm!fw}f7k~&D z6o7qaa4TX<~U}!YPjN&?oJgWWcl%^Pw`WY%mKs`aRn;i-ZujM zg&(d4LF8@nDA_;l(oas}pGib}WkBFII-!x~>*HL6>zn>pp#f%ECRmI}dXa8l#7k~*`fE3wg2Lzw@4)lon6bg@u zvJS0o_7CazUWgo4gv6)MMvRIUsED<4E;pW)TSG+?i`(mI;)W~#d=c; zH>luXpq4WeFl3=UT}cgj$^yh*v&azL3AJX0BKr`*KNy`S@U9RGoeYeV+M_FR z!*$_$q5w!lUJ+lh{Q%J0mj|>aIbVE;vUw63ka5i-p&HG)s=Sf94e&c$P22HBk9-!q zw!>( z&A4{mt@kt}LCBq#83RFpCJIuW87~Jg@QXOjIs$bgVjlkuqzLGJ0FXUTVj11P+tE-{ zbYPNYXlZjXY5%q~p0a!QY>J|iyS`?C&a*|j!SsqD-7S@TQsLqbebF}cNQjAnuoBSU zBGe5D-*W)^7voF@4pRbcCKi+D|5EY!Yt_bYxd`Z?+Z|cQ^D-zOBlKcb^}Ka>Ulrpi ziI?ugT4FqaOJjVz<#hO8b>#AV0E%vH3V%SZ*~>gW4HTYO-!t(tw8GtcQ}heP`kQR) z17)p)yqp{7Ao!`%n1F>HKy{1F)h_LPBq(2Jb-i%uzj6?hzL5xm$(!_Jga9zqU;r?V zpT(2E4z}Ze zU(ugyD@NH6;i6I?7W~a(elkUZsDgGZ1|N?9PW?ZA666fv&lxvIY5x9L{Hy(-{tIN? zpR4`r2Yx}1mSDhFU90S4`JeZ4dEOg^EEkJW{nHlzSodFva*8g|-gHV_^(X9PCJOl- z{na^I@UOn{<2}F!k&wSEcSi2rIC zp}amjeibH#)}$yYwpW>s{jgs3wXk+cO=XZ;gQu2(^GFT_KkuV(`DO@N%xe>~j7EUo zgkOER$p1}xe42;_oDS-nsOV@>Hc<%E0;?i7UR5)6rS_yzR z{L{XXNmMhqaF@t;fWlu8A9(&7Y!A0c@V$WbY_0`a016Q}Q7V8oq797t!j+EW16p0@ z*{@yoLc*_Uu7={@Z>lfD=;Uz~8C$-iaYbM8+9Ni5|J+z<2;cqw)5?BC(LFHEuLWJ*me9S9`#=e1}FopNf zg(?7Wh2$G%tHNC;F^*8khTr&im{#f?^iwf{9`SDTzlnPq5bmjF*P_i|~_ z!F`q-yYKNGDEbNp7G+cxt>L4e9tMJ66@h-j6|`zOous-swh7M$=8K0E z5PI*Bhi%95Q*h*)Q1uU<=&=ImhwP~{Z^e#G$pl)w$q&-ec|#F@g8*%KVRMW`?0yb|D;_?!gdV~1Woc_b2 z=-X};MVSzr2QU=-{V*B!uC~m3vSr0Q8N#jU2l<>X*#=JU>tNeVjL+}_6G(y3uiP6} zdp|Qh+(M-eNo~J*3Y64urrkBdz+bBM)}%2fd#1N2JT3Q2^I#7G#Y|?P%j1eL`Js+R zqu1Wu>QQ}EKcI{bSsq5hZWx09#@*k(L}M1a_}dTsWU)h0QQP)@FW22)uJ4zp05$bz z(_dPppH%zLlVn@7F{Ogr@ag%Y=Bq$zAL}bgqNB7vts7$fw3xqHt&p8Wr^7*o_)+n za((FSm|`I*O}}(Vz-TiW@I$GLn2(;q7|W`Nf48Qem$-|&$ z-qVipDDW?7_k$`v^XNfX@pDQ{ob0d1nsLt`57@IzKE!#i548lkVo*@BXc-Td2%-cS z>GSWuwuJ5DXAapJb}67=)92Jo0n);BQ{at}jU6P;K10 z?fBg%F!>c>fX33OIRQ-k{6E4TZggQ6E5w=^oRUSxt?AwGwpp$J7=oOvgj`$FVo(na zFnj`t&h9%D`7bRx{~-VU$MEEd#=#bhx>8NOvOw3e-icW52BT#h(@&N zdHz91mFa$*)$n}~VQ!`f`FUjY%eDE~;G5atv;^1T*ooq;8jw_kPyoLGJODsuHMiS1 zZ|4UQV>+*o$Cdi;i!7oz$V`KBM}3cKZ9=?W;IzT0|8ozx+WA$HQ7=(AL@B z%NqIU5eLh6-xE|n3;Unk6{UGMIVZkZL@JQVXwxYQkl6h+oM>$E7sbosSj)Do>wJ|vE^&)xG6hCT<@ zC>9``Z`)v>n=GWyg7qopZB~FfIWZuozvK;S>C_+5kK+@JKXy&n)hXTL6R@E|ecERd zKrkQF-5x(5nc%~N!DsV`vn<7vz(vvtviBA{0ykJ#iZEAz!1=GM{QIK)Fo_D@zFt*f zl~%lUAH;&|2e7Kg60pziLCIw2$Y`PQAbI(cfdV~XV4G$LDYxhI5(9%hsFm)~ALg|BUg{UN!it9XG)1Pm zqHUD1cP+~&t~IPUQNbP&d3Im=nbbdTA`}}UWJ_KpmZ8RP zutCf-_h%N=HucLQl#>allKVVM#qX;oTdn<0-)~}yjh1R_+Ky4WlwZoo)(Uzv){Qz2 zsL4=a+()&?*UkmI62nTs!prc5rLCF6c&0_%Bj~enPXw+gnAf=$5>rx$ydHjT6aQ7f zGMNC+lCVryw0kIPPc9%(G~_+X-Y!`K%&$XYREnSTl>^oOSC4G_Hl5qAXUaI4$Y+?n zbof3fRK4Ib{5LTrH6iMYBy)L>bT|ngbltlE*ie8Umb>FWZWqjajwT?9SCqj~89Zx_ zyIYKMva%Md&Z}YnQ98kS44Zz13<`Q6UGs8y_~}P1D1J zTL5wrij{86J$-#_t6IYX;y$3jMe-Wb;Nqmw(1G-qS4CLWt*2C3Veu)gYT2@T1Iznd z;NLI^!0IX=hDU{1RI~-xy*`_>Xi~vE`0%*x@_Z#A)8t;>*S-%014fRiVoNQ*xr9T( zAWaD^h>VOhF?!BNS2n-SZ;U$mQ*;z9<`~H`M=`Rb)HRIPCX@LM8GmCFjG{m+ADF!C z-gl6T>o8m+Nz^bjy@RsR(tuqMBA698|1JqW>cq90td+Ikw+ekgn6YO4MKLdW*}uEg zmU};sCU;IbiIJC8%szT(XT=rJ1doHs8r70wy-Ro`w{I{~+(u&I>yvtDI?6g#F#%#noI+|S z1t=78&!)Wo1SNJUMrKx3)yDu(o!H|`J?rS5?45lUVpsR}ocfGTKc{D~sBEAeBI&uF z*q7}@yyh21?GTw^@ap0u9{~m?{ti(c> zMEboVho(D3cb_y1d`{qVd4>s$EcoLb6{78Vma46zGnQHOUWGJa?5sKZrZ6z_iUV-J zBcDV?F@ry#JS>niFs`MoYq1G{)1VH-&{5blwzMu>8?D!H-WZR2H>*U16PGq0+PL(7 z?g6XNo~8yv9U|6lE-7_@L5-ayDtiVtAzYqg&$37j5QXnujz;fpP7Uijr(xgm21_2Z zs*0kb8XL)m%ku@^o~#EZ^J{a4)93QydUcnK!G7(e<^5=3n05Joc;&7Eis?__Gs#rA z#Z{@*{Gs!E@&_2i))q;J1Xc{jUNTUJ8R#2E%Zw@E)4`IhoE5V9S)}L_s{BfDQ$&Zc zEda~&iuy4ZTd;AGH_n>5nQa+Kl4pZo2r0c`vvSALyGQ_tuF|v4YC^Rg9$#>5beH;< z0&_*pJ3!^-;dmS`0C@r2Xy7MvIRLqjNK9wzDF!N-UYCJ-4%?U4xmUij@Ed+n485+Q zpmL3zvYS{wn&3u@i?#);MVf z&VxOwJ0PiGQue5eSSH3yEMY6LTQ?qfl{N)=ikHUg)M;;9!jnbQ(Qc9ggYn)qyuT;r z0oP|_%958|QMP3_%>|znwZ2rw0l+i=;WGl#afZo*&pY6ob}1^Rb?M$k$(a7{hxs7~ zpe|D>cXj|szRd4#<1!?$zvxz$hU}$XD(X`LFW9Luq_5TPoit`!hv|LYvr~r48u8*O zfk4N*+Lpze9m9Kjkp6+MOTIZeI_M@fA1G%D0V~kcGkNMKWzKi6PW>W;G*ZVp@1o6# z(F-*dxsP6=BfG*oy;$QHVVd9MSl2;SoANk>i)yL>3-Iy~8{hPt9VgxvVNG72L z3>L?gOdVc1R+lAvg&Pc7ivxXT`tvjPz)q-hFpd(oyN?X>?=N!!WwHekiYcDq&nO|Q z`z=ikb+;pO6e3;^Vlj%qSg}g`2E!$7ogbw=jF}sl;yx)_4&WR9VR-)GvwdGoD-ev( zBW9R@oK~E-CaLyzSAs0-8?&;vFFFn4@hJI3x=p$rQP%y0xJSHZFUNhwF;{y_r72at zk*&AFeni#W$|7{xpDIk{mglfhWJK40%)vkZ^5HebDV__y{x;!dQbFd(DqFD zXIZad^#uH)Lf0rASmK|B8oxZwdI$l}O}}?e2S{#_py5bE4<=`Q=P3Adb#Rk~g0glA zuGpber$rIX<)KAy6J|0Yh70^Fhi${Di=h3+`}9SZ@~xWe5_`Shg2Xw(Kq?4lWjH`t zAfHKheATj$loxRj(XlPZjV--*n(-6^^{)zSv(07TfMJ|tRNSxi*9_Cy-cRW3>zj3} z%}4@xCi80UZ-VmEOAs6|sVkz8jW@ukcj{?2r^r-$^>5y2_JE&QoC(Gg!LmW+B8&nX zOFxE$GW&OJk_Y=`mWHcW7kVO+eKoEe>PHkYEdCZgI*u3!0NvEw0i(%w9d)czacqG# zVHRtyage4~eH}Sw`Rvv^-#OfOGxibm9l6W4`7ELt##aacq6SVWRY@cDclMjKM7yc( z76(l6CUgDHwFi7t)Fgy%cL$&jW{Cz3d3E(iq@W(|mtoS8{4v~JbeF9X2k?JFgU|u@ zg-5<1d6pzR54n3oTWiP8|D%n$xzbUM$qk`={Mq zdIAWV=(VnOzyuv*q7D-sByIWLx)z6jrx=Sf|9Mz&F7t=AyrnJel!{7L2aslOHrU>n*<*IU3K=}gv8GxNHY z?*ZHGi4B48u5T&`BRh^owVDTPrQ1*~#e@INW_4)5m1C|APFg`Q_LIQ=&GLZuQCCaJ zrXOSg8U>;s5q|~CuLe;HE0qdeK96gF>sfA-P9h-qbdsa)xlLMMrsy^7%3jzECuRNg z`*EF6Q~(W+yK@`hY^L`y=R0d+Ac{vh{OqVCHLw)An>}(bZZJ@hyXbwaC=a;VU^(l0 zPAJrWe`P(4>VQ+b!0W*^d|sW6Sgd!R*gNSFGK*$^c>AEi^2-~t zZmuGtP%VsiffdbvU;+F}27kOqeHF04#xoL6WB}2w2c-v4vK7YE(`%s?@{;YLs_Kfi zq{Y}mXR(cKFtX zVcXTtMjBDWHv7NL7F;$80~X2Ve(M@=YbV6`uFP3&{3WeaP8_~d$3?d0^6kHRCgG3Oh@C%ARjUR^aHOd})Wzho5w zPyPE<>S7}kAny^VEN)lH&#M_@F0~N9q>rqz*3;Lh?hzK`ZK^kK$@s&_utJ z`(`aj);vYvMLqIB|Av1PDy{UdmX|sZrLtQep8!dVP(5mVv)95Ol=t2W+&IrzI)YPE znpNG|V8d?8MZfgO|M+Eh31CgOZyo-u2L0n7|Nii$>u6pf<1Z6X=3#>Rj_cQFf^L&1 zHcN3F=Mm&(?9CcKWz?{qKZN-oe}x_cHq!o#gzF;46`jRJ{S#vB^LGG3p2?2H?%@?9bX&@akExgfE4LFB}n>l3ua z<}Y5=_W{xk!C=9Xd)D@5jRcSOdxGoNd-AaXMnVi0){YDw&aU69dio^4U|d0fr6&1W z2I^Y`m(^eCiDt=oa^(noz+UUpM8lCHoH$d%u0Jph^YgQ!V3@m~nu_W1(LYenBdZiT zlaIOHc+RH3lIc%!0@VS)c)88UPI6vyr#7mKw>vSpo@Ar$oaSFNiiHUiSF{SL9Rff( z+pNM`l(~Kx9%tM1*hTsYtP&=Pmv3e_mRsKzKlZt~UKmq;6~?A;(d6H+H9$U>ZxXhM zT=k64sRaq-O^agyMbDtFDcCgDcZ|_6aI{0HSDeIIIzuj?g z!+k^X8vzIh7%0~kL4{>7VsxrHp-z+bofY#%jq6l^d5Ov)_SjOc;!^(t?29F}{qgDY zUAA9R~jsuHPr1wP^Uf6nIGM z55EbOK<|&*uL&^M9QAKQa6NLub5jgQEV6Vj8^wJxe(t1ruR=JoR)o{`l(FMbz;Py6qGG z{x&pQ_8t8F>#oPBE2mHdV1u?#uXEq~oe`pTK(8O_ye(|Q`{C_Thm)N0_d{}m@mZs< zHlmKEe%pm!U|RLMfh1xgFd3DiWg`R*bT;UnYrmf&&WF~dGP3{R)^`YeKhVeH*?cdN zpsNCefI)Ae0u<_Shb;~pwRDuV2`xjXU-XcbH0XBD^+v zvKRK<0esr!%(1V9jlHNi0{fe#?1M1P!z7hgdstG&`S`a*YW>Pxr5}R%Qu>F7h+0es zuci?rC@fA-Dy@}N22yC?Hh2N2dS!11BO}&7f3ULz&_zftN;ApgA&aF_7Q)Y*9}dsQ-3Y zK#7n@&<((v1Bga|y;fG{4q31tx|uTWl`c*mV6SX#@0FsEyb!^fL)bdeJ~p3+$uQru zCPv#({(dQGF?S|svt9jtjt~sPicwr7IVr@v)B{pjxaprF%Q3~EcTT|a~e7CQ)xve zJojto$LE$$`IlIVTGVN~jmoo}iq9n`%#>x1wp&XR`#0BFHpE(RS{FORT2jSsmngow zC!spZL+6y~=Cun~-24PEeS=P#|J_m@mdndStK~)cu_p^VgDPcQIH1DZK-|%58-~vX zK1!r$AUlQgt_SEi)I`=GpD}McOeV= zk)O^(%9B(@8?kyBn?wz^7m7>arcbSbJFBY3Q%jCLk$W-SJ6=0FIen3u2`i9x(LTpk zL6t=djogbBr_|?_p1<+Y@PV?hgED{jm9B9tG(5DzcZOq5fxsNs7h-p3m_$$4N zm&y0i1do7+bK9t^WK_4qotEo9sEvy zkzhw{A^itQt7Lce5>vLf-y$jMw&Hfy%P5k_AhoQ+qxDVDOu>!Kz4e`PxEgf54f=67 z9@76iKg`F2=vAi5wp(-uJ0k|qbS9y5srg_GZvI+q&qw7+TO6&#qpI9Qo~g3&Xo#DT z>1&>nlMw?Y)@YQq@^Me`;=jj+qepKULk_+Pj&4uR8LltA%cK>zYMe4Lc52Z?`N1|) zz?Nsp>G;@MDJmY`#Y1R$c;B;i;I`W{a}QqH zB!9nqYsI$iHm>C0ojc8E_L`qMT%-eUJgDh>R6{YvD<7l(bm4l%wWRhL8?`60i+O|Y zs!lyyf}I$)PhtJYTru%NfqizvSxSd=x0*$=>Z%t1Z1hO$T(%0+?lRa)y5V4Vg(jOl zx)o)&$dCri>#1Ub-e!EWX@|27cyEUQQaB0cnnR9`rVi+l=RM9k&ZFWRj2umX6p87Z zbkWM(M2K=ZytW1-l;_Eu3UhZIcdkp?e;#N-+z(k5m}9Z85|b~-gC>8lygy}%u!p?p zT$G%$JXW(laZS4T#Y@Yf$Ae1knx+u(79PN7x}I{U1fH{{vA)Gg(?qp^2K%ZOT7S7pgQ!_fFq2q`V~RCtwZ(D&4g2X{8H4>;=wQ7(CsHen z*&no+p+qjuR&G%-&2;+-2B(J&M6t$bWovRAOSQ!1Re1&qbh3G&sqCIV??rw!1O-Gt#~9o){zB%~jV(?S+RD z4@e_rhbXzx%EmhSUgZi2KuN~puOjV+m+u~*5a~ZgSHMoL6Vru$d2d*Ln%f~fk|^Y) zAfZ~6O+&XANRg3je@b@)y*l&3E;%fENvF(huOm3+wN}dVn!Mu~F0|8O!tKfHaWfW+ zDsen_TZ$S_rEt+dFMLYel3ps)`q;Z)-!gjV)N{qGYlsE<%hMuxlX)N$azSxE)uj>dP4Jg7{@NpJUU z(OW{#zUi2HzBe2`M({F#9fqJQX&ftaHbePvS}(^u^Qw^lN7+|LMfG)mD*^&aNGh!& zARr*!Ac%CAG)TwLFw}skfQpnzcf$}P-61GQ4m~s?F?7QKL%bKi))T*Ht@j_+T{Fx* z=j{0GIQ#4~2Ey02iFg1IyImu>YCYudMv(u3KJ+@6_2+G(I#1vNyYeQLxMQOSE6R1l zp9=CZc6$$0vu_r4b6ZPf5u&0|sie@f%-Qs))N8M`FCTCFL=wfY_an(9YF0nX?lTn0 zW+H5}X&Q`J@7N4R1o{Yypdjx0tb8_G(s0D;e!EdYJ2Jt`nCtFw!RW0J8evJTs`8x} zZG0Dglt{S{JR4RiMI@Pv$$|^Uy>NhvrXoT)ZRf^hSsJ} zS5NKMG(Jur0>N&jy(Ze)RIYbUu9;fIhd;2qi$K?I(xiZWnpZH&3NvZ_ zCp8^Pd)6zu1;9D@?Cuag!{~(a+UTHw^09|2Cws`X*gIXaD}v6Kd3FoeD!d!Q#{3%h z^&ou5iWwV4OqO~1u!g2ngU^2AEsYOTSs}%IH?wD?lVL^$uo0uw0&N1<1YO%fF=1if zA_GelWPktbZfSc|1GEmLk|6H@>?3~#8bnl3w9=3%i3!vv+e@{o@hy=Yrq)@U>!^TP zxR>oFX3kPw^VuhKTsE-OrVhhJl}`|-4D~87P~*WJL8qg!3@vCm@drM9;x9oJJPhUq z63bo>svw9OW!Nx_e5K9T`v-bHipC25XU}CJOWqCOF2Z604&J=>kf#03l-o?A+B|5_ z`^TMR1D28&-iB)p!weq1-iFJ=h!Ahj^4w_=BvQUye+4zQ zBItPTE{=D#P|pu#tvTZj`o<3qhFz;KkpN$mc>bRTKA@2Nbs<&saI3jfAidMjM_E~G z@Ovmn3AsXdjnzB+by2U^U&|)U;GDw=iib$`GR1hGv6{qgbyJnXYeBIp=;D0yOtSnn z7ck|Mems?No{!G6yO9#ZTpH=iP^F@r9mPJ~5#=i)lrAnPN162YXPbb1_*&sT?Ulnd z_)=k=>`rpU>ph||7r2xZ+c4wFoRNHwD51OeCrsN(1+pjJcpxothhlzHGElAC=Gyfq#}&eZp2PH!_*cfVXVw~?N zL}(kjr^+nf!!~igLd&E~Ge#{K+&g3^o zDSKZ>u37Hfc+Lb!@yd;9M}iH`nWAgP4$&Ra>6fC|W05>0LGKJ(oMVs9zHHcu3m=8t zs&hxc+tyiuP5G;H%ozFCF!>$vQVldkr|iYa{5C7nha>C-pu=o$5GP&6uT-ujSfB|` z<{6hYc-hNgrT&L;4j+8c!Lukp#ANjR=xSoM%UJx*lebF!vSZI2ZE6>Zk-kMwv(xW` zY8j@^3fSN2$*aH~*@wN zpGW*;)hWEU;e5MNV@w_lbUHi9%f@q;lb~7~*cv^fI)C6&v^}t@fEo@tXU(CE#Z|L; z-Oc_mZ+9g7#9r9xOKdi%hY~$fGt#TyZul%l#J&+tF;MAIgEja7?vOq@w84Uh- zv3vbHLHqAdKy1Yj`F`Pwh3656$rT35r?R>0e4pNgB?NKSwLGc2`V0j2c5Z9(3rQFQa&jYuce;77fd0jz%za+ZhB5k=cWw2;!*nQ3||l}{0Zwz z-I+Wqvlmhxa*eOVa+*@>>Ad(1lQYrBWo?heHsAQV=L_pEtiD~2^^Qjs_74cu3;HMcxRcxTBVQ8HdjE^w7~VlIDDQbk76)eb<}9ZD33IR>lI$$TYC$%_ z2}56{FSE!leAnpOeMq=Lf@@@M3vCL~b?Kej-0a@bt5hF~l!ENoqm46q?FgUO*Y%ua z+h#Ov@;(aVWxT{&4O3aXj%XSWCCpsnGD+z%DELH96|pVCbVWFv%e8cUjDdi%rN&vC z`LoOWc%L;c$hb^KHX&kULYTqNP$PEKRLAuzXJ-rM^Ds(UN*_rnSK*?=tXX#a=pHIF zD5)It+PqQDmM+PwUHipbNv2R$i;QKfuYO^~`?*>vJUr$T%Z&6E6J-^Vryi*g!|!?{ zaIr)wrEKV-3cu4Z$x5cC7U_m(*B#!u?{q!y%TG$F-|vjOJEt zWBInKCH#s9)4n;=N0R0pZKKG@HTAID7w50a6AS8`GHSeIC?SZTm7)DzMg4UfsF*K_3?qy!<=Ah zaA(&>6^h?1pptGn%wYDo3kJDtJFqqTQ z|D1Beo5oyHy;8~6Kc;R-yvJukjB6iyU=?pM_w0&7y#1^KZiwVWo0orMxY>(u+M z8eV@nx+Rx!Z&8R+Atwyxp`|b1p23O&2Pa|I#7L~lryACHm{YWyTydx)V8A7Gw|M># zv7#Otd@4qCCWy|KIJ}Ne@?aO|p~XvA&j=fb>Q-wTqm`s$tz2wz+SmHis+pnJ; z^}ULxMc3zBTgH6udK`i;W4Mp#)+FfqJi2t{qWgIQFTY6gw-vJ4b#toPtjRN2w`+{M zYicf8HsF}6A5Wrva!Wrres)65Zh1_^^vtfi@vU*Wy(xC)pjaZHxF^(9hpLOcyEfEd z@adznAzb?3O~>ChMS|d3mUbyaFw@sQR~d1&HJCD2oMDLyG&Up~X+d*+ESX@lV?LFlIij_dD{7A` zSw#)&e*dUakYwJtn;V}xW+^9&P9Etr6{N%cz&2|#rvk!#DA+KB-?O48MsS7R^XZColuV;rwuC9xry|Zu)8EjpH z9*5P$vbe@%aRU~$!o~us=|?=p&~utxQvFOXl7y|_sl!2MIE#1vQRbEl1h>t_96ey> zU7ZsWT}?cBr(w#{Mg$*hqZQeW=}63Hh%fJ7ds~+0JX$tymG88$O;bwm zSqr!lzyGanes>Ab5(@zyWtE6h0Xwg}VwjCEyduxr)>GI+>ZQa%JMFdNlRgLe z{`rpC_0iDpBr4Z7Z42azD76n?IE<<$e#_ZchD$TDjHZ_l``PkKOIf^0*dNgO zc{ujJs{+vA2-r_pkX!sDQl9Wm8{D5m1}2AmA{9^|)~lJ{x>Gao>VLp9myS$CMJ&rTd7TZ!capgE-8}0$?j2DGKn|# z#-#z))F;=A<5ol+*SQZviIK>X!8@jP@08%Lg4~uxCuoK5AJcBGy?eJEbZgpd$PS+H zo#eE>PCQsxQKEz9jtgBGY7p_*6=Al4r;YHwb!#G%*tght>}Gr{vcmhyV0~LLTn@Dg zZr`n;tO8G@HVnQAY8OdhH;gO%5|p_pV#e~R;%fJM!*<}9udH#i|C;mnrVXaqYx=34 z^cOLpl)$}Qc}j3NB>RTe0(OD`)St zC$$w>bv9SDRq21GI}1WRiM*xLzc$xer-vANRpsNFxj)<|!t+{N#Oz5+dS0#=3Ha;% ztrqUtU4`Bzp8ECaKCgnfC0y&W;gcS1hXlJ9w|FUz zlj#F}#3(UqR?EKZGFWHf2&O5LTLK*I*p8_O)V@Y^@2Na`VLLszn3;L$oPs0%4`}g$aGM zcVZAvQqg3!SkpG`w|(y_nIL+<&Q%6o8c}JC9QH*db%H#sUTgxFthb}oIR-Zai>R~b z7^p2CdP&oBW9W6rwenN8+^?H^bid&=(y2WF?YE*CfVU}IIxBu=e*frG^7r%cXm#)X zeP=6M6B%e*Flqv-NtuM#h)yUoj_n+@1G(S#aY=<4lNVbLA5;^l$(LGXS5dynIeY3sk%Bl5 zT))8iUP4tv+ImgyTFx%zqz;{*F^`>tXCnUp@U122DKPKkzc#q}z^vQWokKPWzt+c3;Da!;U>Rcbc)r#+RAez#Di>hu<)mUBScl(6La3JP}lsv}-{!;DmA? z>5BaL#x?p@S2nDo^9tkh%sCt*^*bC1Q00(`SaNhJZe4Y&RtL*duh9HfNOHN8PPog4 zDa%7cN2uL0kfT_QRs2#=v7Sy+^-{>)eA#V4>b&n#fB1}DG?kTf$|n-WuUT-X_g;(} zC3ZNAx(Ke&XN!=r7ZOXmW^DzxQ@WH?R~8PkZ%!dr>dmH7I3ZI4tUPKB=VXI#Ep%K} zp>*6@uN7zR^Sc&f$M2nu`#DG|?DlZERN4?ZAhFWn88mZI>!w9xjF)m17{?c1Aq-F> zTZz3LemZ;HP_g5q6c9#`-l<$c>DY=%B^?@io2w`-2VwVGwaKnd`8rL-41Uq`gT7g*eG{R7mxaB8z?ttA5m}oWcR3^IuAuYqVOcm{s<+ zk&;plKs%6~S2;K>7DcDLuoP?^x#!sCVpHjqPIkfEk zsRzT;2et}L#m@_q8rW&HK`nUTo(ExgmZO~SY09-1`oFxecxUBPvq2wc-qr;s2mWjd;e#eA~&v(;kAl<)GBz;xWWenoCLh-cVTe(z1JBtHWABp z+)@XWYl3cW`dNe|np8WUAwzL#k=$b3Pl^iX9dN~0+oYRV5A+(IKy&gcB-5R@|18LYt$4tD?NrR#GY(53TbG7n4+< ztD{!;DLu=Oj8tWHbd@(GfUSQ}PJ%|)m2*VC`UhngrM8d>1|Q#2mMK$_AL#@SH5pa? z0E>DFVr73L=k{%Utg%jwgE;Ka1XzQteB-GUll6R}kks3r@VezBo>yc(Md+mIQ3t<6 zKV&84`+=O{w3Eg5DYt2(PuZ2ccJs|-#xY{z4IDgpz#k|couLsrrRlyV?4frOd zOQxKpvCnZs4J;&y^rDx?^QDz}lO8RKyY;7aQ<5Bej$v{eaf_D(+|(g-PNYnEYFB*v z9yemNv7ESno$@1Bv>h>T!^Qy_$fe`+9l;*Wv;n&c8?2mI{>m>}v@Qe*XZ8zJ`fBZC zbi3jD)7+QYNb37L1#uH=;DPX}PF|^r`8yg%a;#UzwsXzWZ|7e{RCefEF7w!>p@z0a zO;~b{ev7<&VC(3y{zm&u zpN-x(i*#X|jNBe&*V7xCXa}vzJoQy+4WhrH7bvTcNfVonnCM1o12Pj?-`ROIB3D>y>?5&j-)u_I;qP;Ku5P@yv$!I!S;QpzLc(T5KxfIl82`kQacMm-z z?nVd!iO`*TZ7_CEW?b>D&I*$i!yzDHn->`Air6g;Su8MDyc4tx?cU)1+N}> zhbwwicl-;IlFhm&)E+<|M4uL=OSB9<61z3bL45LcKFc5U0(<7n=Rga)bDp~4xORli z!m{8z=~TU0`0Kp;Z8XtW?tpK+=?e=NzI}g6qvar;6BdV)D)skJ=>B~l%y~FiW*43{&eDPeP3}1is)rjE7_=1{ZZyW^v~I7 zpv06Yf+gZ=56}OY25_))A) z-~RzrK-I7SJ%z1OC!y|2>WIFC9VH~S-VHI-DnBeTyMW4~ZQjpivq>q!Z{f?VS@M)M zkD0DzTNbGMwR%3Kc7nFf#_H9$ZECI(wxZQR(VE=N*o{NtWaGL9T|Ni=2NnajkdIFn zJN;0uA<4id8J+*r>`C|aSLM0$XUa7|-ZyMow^Ahr=J@_Nrw{D7Qq3g@+PH+eZ~sq0 z_BVh(-(UVH_4>d5%;y6)$)xyv)S20SV?RZM*O}#l$R?y)Ov<@Th%aL^sDsaY5AUS_ zQ}!X@Ix?}U$hEg|Z?|ts&7A?oJ{3LA-!jVeWY8JmxIt-8OT4!kLjE!YP zbnnEb3f6y137mKix?od)XF$fG;o5&MWBMhwUnwr*%PG-_wzvwh+E#s)Nv!mwHM4@_ zP}{O_hu$>!KB;J2fk%iRU_kNu)Idkut$`(hx8G^#OI2Ygo=oUXO_l|VbRYYv;XC5K zZc!p92F%r#u(=<}@3fCtdGuPN@YJ#YQQ|If8AnwPYDKpLgC)l8;JXVp1UI=x_?ok< zQS&$4x!bjx?dz)iI4u#gcGnfsKBzA(r8>R{zr{RLm&bC_P&1eRVD_z16W8T@j$|$Y z-@?Qk`KliRD0l~`GnSzxZT;^$@N@OGcL4cg^w8kL@lbXQPOl5pVtc;)SyfloN%USNA@k z8p`Zb>&vsmCXw}b(F(PfVUPI-8)E@!jZWng%+iVIth% zR9v*+1=-Jhc**5JLUZ_`#DWVh0OtFA&^_bP(U#m37JxW0L*~QzlDBQ!3*PO9Y>o(` z;)IIXW|5Mc?&qm@72sd|rt8<*uHRoCI;lvAsywq?t_y)vFWR@CNfl8PjWSgTONzM? z9jw_!--_x=b|Bz`YqDy6sg&yW!3gmwAfor7=y2)zv)f@uyvb&tJJ zpVyzUwcW}o(~gICC$iH;A6r;$4b6Z%Uo$DpA9IAu-+WzyY-=2GaEu9Y)TJ_lcGDun zI8IMEnZBB%fYO`WiN+!g(kWcCIim;9e+HBEMd}VCb!hhJu`*Yfg{dj+&}&($tuRKZaJfW_B7wV zI@VX%+ReCT&9(ngdLrG}{Bjm#)pN7nyB_0q~?T zfL&?vp6(|9B{#oi$y+upyG9q|LD9g_sEJ?3`HSq}pk~m8o)?lcuS^*G4ibRdmR5Dx z?@fTs_dRb>V;nK6s;S?(avUvZXqHzR?&HU5E2U+e6Mj%^J}(x#zCq!1)qc-`vNWHE z%)w7hjsAs3O18OX4ePzO2`6}U2@6TDrlyl z$CG!y!d)kXq*tEwJ#=d94!>x1zaaQ?n)>}s9n#@cY{z<*hc?YJFGVVsAq9&2ch}*1 zr(69>>NBI16r*ZUu1!@!)P*ekZBae@<3o=Vu^SKK?Hn7PCG9q5`d6nWhb;iN=c#Ib zxeJz%-2{K1jaH5oGHS>bg3RTv&M(s`DN`~IhLo?wVrTM9W{V5a!7hy(&%g)?(Q71k zQYlbR_c3WUZ<`$pd2Ae^Hg%FXlgKv&0`XCk`)-ilyFF#&r3GX0RoKHqgKpCvxMB@{ z$B$@&yM$cnihlqm(2WB_y$02N^IvbWy|4x5>CNoB6zF}QgU;f0VGCo!23+Yc=N4w< z1c|pV%v}SMs@gr5HmpcAZ06>eB~W$jZ3vu)1&;Bni*nVDE5Qg0o3YbO3BhD3i5#~` zDnzEbHitS;bmQ}{yd8X=g(Nu-JRvUr>Bcc)dzLeFdbhnPe|?YG zg0j4y^F1F-UX6e9G{1hUmlkpDr^AVrdPwNe%gtd-)IhfR1<48XscH#iSWw39_g4We8<-ST{&D?-qJtDv=&ke z?%qnS+GvgplL?(d4T^yRA8;^H9oUY}u)Ilnw&}CdeWolRj}iy5*m!A;9m^;!iBI?W zjAxU&vmLnRJSsYGv^6-4JIHPad9LAVtSc6n)F3mES%YpJkA%T6^jJ;qU~Yd=Uiezr&J6I@J;TWj&fznr&#X^N)~ zlIoaQLMZCR(}~V3ifJz;+pZj&hz>rOtwL_kEqWiIHHsdY+)JJ&ZnCiY7_KtNQbCZR zeIO0hq;s&W8yY!7$!dH8PjGtO34xS?fbLbeBz`45cHJ}F0eM(-8}2Q#{(g4hRub|s zaB$^bLzn`pr^zq6F*@OZC}y|qq!pB{Krp@)E?T_Fd$`*(F(RmqI`{GskjQ$(T{M!V z^isDZ{Xs(&cJZDb2wvaFTI`RMK#MstStH98$2s#80|mz;|quq+B1JUTtDeN*ZSiF(I3QR3lkP@unzd?Id@>R_jA zGbTRazqU#@JxMd|KIL_HZX?%q#u{q9>=K^OS5;$_d7B^h{3-O*`A1(NiL;9iCQVkI z?|xD8H}a%WVYw~W;};;hf)dTr`V+MyK*D_U+&11)CX zM^~vFeaktho8ukZvOgt^=0L>v=t#1AG3$sc9`6pHx!tb>hUofO1M`P5lHv?JAGBGV zHd1;iu2&*kqzxg~mE3|U-0A9tAYZ5%#R}b~#lK-gWg@BYW}G|6C90@xUh7t;5r3P- zZc6@2)~AZ6urifUwg4-b!U43h188su-=(s8tP`w}IBKGq;gfxQv|no^<)(LAG4Yro zVeX#QbtOUiY_j~q8XajCnDfUU4u9I#Mf;6uZmYI`i@1O;ZMc_Ts3l9x5&!g*zuE+J z0B0qak^KG}%~=Qn&POp}dPVu1<^`u~yaBXD5rb+WMbLTovmKSYpVHvnI|Rhb?i%f} zUWUMx-EZ+DEpnpvAzxq3Lv--vPITh#_B_Owtw{`Wu*YJaN-Z9ShTe#hiqf=OVV2kn zuJY&_IJmdgi6#%xSzs34fZoWS<%|k5WHqA!ykBZ}*&0P|F`B8a0}}M{7qhE@S7b}N z*2bEXv7-p&xZql=3G;V%%bcyshBi3Ga}uKPdA|TxZK1jpn<}*uIf3X?jITRpiMsB5 z8d*7})42{jWP$ejb}>2*3|` z6bM(sL3)9!39d3ZWQWc=q^=xgNF$@o7LDLh{a41k<_FixvgT4L;w|0wNI7EKvh zRr#E^%fi|+9!XM#4L;YkCo9h4|3-fi5J@}&bc`jhy~q4NqCI+=fMZej(w)HBG_<95H_N5((k&7D*L!rMO|5nU7p=va%>%w8Qb}equm}Pimxz{q#)bpCKTY0P zat_#^0N=xxq2YD*4G(`q_2gQ0tn=Vo@iJ&vrplJ5PCsmSHf3~U@^aCrS$_RyCzpNx zUg}cP^hcNQFYL~_XuthQ4?#7v!3>6pQ;xz zXJ3ZFX+-;UQPqztt4wrQP^(#yO(lA`6BZ@+hE9-BSt?X2#(PR*h!~!`!g@!$)?U9>dqJN z`%u>?`pCgAYXq0XhAh8ebCTZwP_2SQU7iO%xOkoDv_B_+(J*Ku)_;`JXk;U%oxiJ0 zyja=IYx2xX=72Cb-VcQ`R3>AstDkZ>qERf7O#I=PzWF)mr zWS@#H{KB5SBqh zEjl)pi$*gc2|VtlNyjR=T{qfkr3IkPVs{mPHlj2jr>BkQzysNcJuudtW##ZY!x3HL$$& z_2u2+8aLhg$30=X?VH15+}xVd2aF_~K&E8e?WpQ}cZm@;m<*wO?ZTfNq8;MBmm{Y> zP5XWjs$PPufxVd;SzWIHQvE!9V`llE_Xym_<5{=+0H$DjKdwT2 zj@4EzM>78z4tc_;BK%lhp1C*q&V$^ai3tgE9HOw!7r_yWM7<(>np(1~H>S3S$l(RN zCtP#i2^R{f>=@LX+99*)BwZO8EJlw=i!%0FCVVDiv&)MvlNo+jP7E?8-aj=~adeIe z^MSvL=Ng{WZsI)ksnE)K784t-ZBV*i(zH&R>~F83UpGJ1M!-V1Tv6p`XFnU5?v8J7_Tx*kCqu|CoEE_$O*LennrAUj%GZy5#6$5Y z$E_rLXME(kEzpe?F4xbV@UC_{T_Pu%>G_okl7C^2HtP>Uwj$|PuBg9 ztj2SN=Ag_3%DV~*)dV5hp#vsAyOIFd4HW#fsMQ<}Nt%stb|oX;#TIOie#3lXk98Mz z%kcQf)a7+PLA3nfy$)?isWG`hjlUO2wnig0?rMt*i1+vw4Hq$kA0l2Nc9LYKKzqJ+dsS=us?ccW=_;4MUPo6Yych zb#yW;`W9TD7u-4i>69{Gd5J8`1$~swbNnXyesY`K4_fN7ab^$1e#tbmR>N3WP;>MG zY{D1^Q4-Gs^wkcW8xm>ySnmTfPyCv?MUG`#)TWLxm#BX_r?nc-oixEK2Z}Snaq?1x z{z!Tf)i-Pki;cSg;_j@jGQJH;8L$Bx3V*`g{H(p)FVQP?{!pzg>n6rdZEn~C+L(@} z*l$5hW;1s-Rv$*aI-MN3u36|%_be$l8oMy%)VYG2uTC+|M>W}9`x-ur&ETbQZR=7c z{P%*%;7%gsST5z)($jIoSn%VsD*5C=8``ebAPiWP}wXq+AEJTL65oHXr_Pl zixolCL@_l5>Q+BII4D>BRK%EvL%m0#0cfvr5l%7?Zw*)QVUp08`=jvK{>i|<8~>4k zhoR4Q>UPvL4qS{;y}{EzoQGPKZxp$tUHrN4?KZ?_yY~(D3iPG@T>Te?vY3FTKpp|# zqF}NgZ6&K_f~m4NK0X7h^6>|qMZ24OB`V&UO!8wg2#~*kS#V z#?L7PCf%eA-t~2~pRRin6l-Fw6^x$Yh(M3UnPG(AK7yrlYI2t`4HQy@e{F+*V*&+J)l?m)81o^ zrSqe5^v|{k_yPAbeP3m|%&7@xUN>^8A#K=W0vh+nNM%~);gEi8MzG(b)@lw<&A*$5 zQ4kB0zZoX9^4Tn_P^8RX@RiW7tTxbwe(FZpN`8rJ>Sqi}>i+COuBqBg72!6;tHt(B zGz}iZP+_I|wrPFk60?3=tz&Mc0}1}d&6UZ0-CQc@O51WD?+<@422b_v9R)hYoZ$Vj zhtfNJ%ZwH&N9U=6gHqYzKMZ|rGlGEieXJN__dGhn4#z&{K2aexGYBlg=rVr71DkgP z*dcd0RhNL<^QdCrps8^%s%imf%N}8YkvMYxc(B3V-!+um6x8u9F5Ymxr%)N=18YR?s_jj>rY?Y@U!Fo76pt=l^^XM|PPk%QCQrk9Mbazvngt zVg@%6IB~<7^2p5#4Sa|P{7Uo2Q`#!m^O_1J)1w#h6Mhw)X6{N+65~+2C2Nl(5j`gjhLoW=S+mR0BSpbJ_fz#+D?8S|XubO>{n6a~;J(a)v&%fEK zn4m|3r5sfp9ZcZWz8X(U?d6J%ul`shy+j%<=7Ad>E_luETQawGmZ z2o908+hw`-&9%zUlYp z72!^rqD5&Xzh~C{LE=oMlY5^51+b|3I?f&n6)00BN?i$R5Z_A?5WAw{q;SE>pMy}! znZSG73ml57{4{iyme#DZ@)NH8Gb>I)EOz1Z`j2@H4Lq*1dBlNNe*G@Mi@i?>j&wZ6 z#2Kmqb~#RqoYuZZerKx`Ka{9GmPWiHD}Wd8>fRFjwLCynxbH<^s7F803fJ@L$BLc; zno0lUpfY`6X7N<{Z8y~3tAm)BkKWtgYZG5y{`EZ$Q5^2&%lYxD?kX6VmeSw)0+8@aXZd)XB5`kftvRDz##mT17siqP~T%lA$W zcK++#%9r)mlyY-?04YOah&D91ieX>*IkO8IrSSpnSbcc-KQ6H9d_M1Zru9cg{)`K( zF7z(YM@PWUhVc(&e~`Et6GLTVD%A(PE{#MUyZxg2Z}I~VeNQnk)I%#$ET#VCjTdS& z+Jt_i%1qi;KUk7?x3FR9#Y2eEe|?4L#Ve|EEFb^-D{w?=Hs8EWA&P|c~ldw4MwSHqsS&Gkgqc6NkCMMY+dddz#l zXJ=>sDkq#xSa_#L&o%9Qx=x-v^!cU5f01oh7647!2bKB6){g^54zOx$_G!icb^6UG zWJ{Y}=PIPj{?&cI3HH}R933EpyJpEf?(^+0VIclO8EVGyOVyc zfXxl-jkuVJYTOG7bf-(~W(fLL%K)HY;(&>nwb(YLflCy}7!X|m@14p= ze+%fFnMqwm{LP&ck|ojp#6-9DJ557f=}W)(qo|#>|Dq#hS>%Gmd0GLL3byX%ZPLJy zw=S0UP>-Kc!h{cVe0;pMMfwPIaIiVWO%;y1{uf7V4brNRj~!lJ69)R9G5?m{DAJpH zCknTDs#JFDb8lQoe-AEOJw(Yd~W(;6|F zza+m~`~o2t7^iB}9w2gm+H_#u?=h6D0zV$3q^u3OI<6%>xnK(K-M&hpR~Aib+^r9Z zE(nz)v1%{YT?Vk6lLP~qQpLA6UIOVZLTygyOj^QScbe8<4OSw z^QvCTQB3Q{)GibgF0ZT%l1x`k&`a;5^Vsy|F_?6>$%g|M`2Ak!u1XsEn?{Ws>}dnn z-^;Ycka|#3dcJdRWlJFl-2CwK7tPeTqt6_}zKOoyFK|h7FOC|L?Hdkk@=%tmjbXUh$3vau=qqVcICbegeJNUYDu%m z`~|H5kjPG~x{D7dC$5f%^uqrS;3>HP9v?_g7ywdFOODr|T3MARTN_>rCZ)eQfQj?! zqboX7Fepr##yAcP*7>3FwuL<4&92Ng*-8jx)+_@BR z#O7o&b2hn$a9rv9*EvZLk3tTes{P@O4~>I;yk~#D82?V+IQ=dsKEPj6tWp+r8@q;` zN>_BH(^=rEqzqDbVJhsmzX-T;5&`RkBBb60CwI)gtZ3m2=xh9C7fggOJ!qB)&;)e1 zTnl;+DnGJmXAJ)XdjZE3s*j*&h`#H9wi8u<7u3<-d!-5>uutQ8^1t$$QXviHsWMt; zr{bV)$8JD%9*o;G`t{Rqo^f73( zS`ge;a#yD1RQycsUwx6#yB{H|ty%>#pwW|eZSh(0zo`r;>^erUkMiP%O-KXvn}1|b zwF(eZ_c}Lh<>$?hF$@#guZ6_2_ayI(hON)`g8@erdu5X$8!v@kl7m>#I^rvMvLjug#A)Rn3(5sFvu z>PxB%-ug?rS5!<^8wdfM2wT8zTx_W@U6-c#%m2R+9eNT#&(;&^=_`<1{cD;t|4cDJ zh@2<_I`@U!4Fi*r>EqFkRq#TAHd47)QXO!16MR(uR_guRA5Q~ScRaFjM%#dGI@2jvdB-G)mPKU_5UX1NP(R&5Af8s3Q)X3(ijo z1!iraXQxajM$oRCf4dxMm00Qmw(TS9T33$>QyT|HytOR;iiGGRaI;p_>QYvEjl$5U z8#1(1C{f3s0$`aadiR>eh>@8zqe&%RwT?V6e)Y+wpYosBa1 z+l{x!nFJ#m=7KU)oR4xonvcC7yBTElN95>L+cCiQQ!!K!I|YSeST0Q5eyoBC0s9rA z(gF73SeNzgJ&AAEv6RW)kE0LGW_`fZHwb7vcdn<-1Hk-`{sQ!{l)uo+!(RRk93Q%Z zv#vM$iM1_2z}7u9Hx>KaKmgcz;{s$NUZ_N`>#mLjX@v;0*0okL5sun&{7TQ0noX1 zQK6tQH|o#Wmoefc@MJ(!b>cp{$BlUnq6XzI(w@`zRwXOs)n}$Exv4jnA0%)7l;XTU zt2d#cKaAHOB)w&oTB5s&Qzc$)D9gf>`48cT8YS%33HxyX zVdmui1t|FXn`md*=u8&nUq3|tyxHCnOW z@BC5?Knv43vc%T$5carqwdx1I4FV8u(f3ibDuG2yS=WSp%U9frtDTf&Kjjq~diRda zHuulf>}traP@%+0`HBSj4Jr>N*W7+786{u^q1#wofRARW{cVyL8Jw*U8>o5T7CslQ z1$j#TLEbcuE%HKWeeh4hNPuu~WQW^?Zem@=aAl5wN*ekt3_sj+QywLkQXPiM zS+pwXZ)U>+Lxfu2MzgjG+KrH3(PmMwkZk-D;tGfckiHcn^|x_e>d2KuX#^{KMO{M} zlkyoHyz3iT6bB^{cj5lw!(zGw^k&6svj)&CP{oZW{OEtiJT){xX>X-`+ zDp*8n&Gbhr%+Rmy%HM`IEZb$RXyPe8Fe>F?GpuAgGhRzJ?az?@vMT(fQN@$n@i(Mt zk}U}flC})CI_~om22GO$LXpdb906x%jny#?C0v@u0^<-i>X4>F0gw5mSJ4@rR&mfL zqPv}4bLZbaLhC0U{t_OEWZbMR`E*WIt4zL0J@TcH)LasXN^nXp`fYk%tEebo$pmI2 znDAsQC~`n3qn&PrR`BG~N zQzhfd(0PsPGRb$Uxx}-(y(=;G%(mtNw`VxCIZ_P1eQ&j8xMcdOiTn zA_l<>Ya#lZHkg!;Ol+_p1J4d z>$KO{_jt{I*J5p-a&Z*%PH;85mIY!M)FD4s&>E=i6%SD5;oF!JTcW7<)qO(S3Y9cSFmyk4l|S9LNzPIJESg2AAkcR)NzE@0Jj zbQ_ZG0k}1@Zy-J`{dZ*~QJ=01o-kDXi6A)7G1as477BEN$hH-jZO@9DZvE8K(A!Sm zmP(uunc-iWU!Q*}f2du4W))dEuqp)BnxtJTnS5CRIvzXpvWsq8 zqh|XieClrkXIGs?_Vy0QXIS@8foEru%OY2QlN5;3ZsMhL-aIGPS8&AvDYdpoHNE7v zQ%kET;$<@^XDKrnom3rN+!BXo6TE_my_ziV%jHb;&xNx)$ZAcpV&B}+JW31)@A?T1dJFUaHr=? zX@0)|ysnc)F%+EQ_X~{!Imi8(FBxdPsbS6 zNkJL7)D$KXiwqli*z|{PG(9sB!_MNt2xYuTPTIgUhpnG1Y8YeO-vSGeV~x5lIgL^j zWQ8J1ZPhuZhb+1pwj=gPF7k;O>aLBL*uUQYd!XMBfImtaR(twseOC4a>(~=C)w$pt zQdSo7DQ~;CU*$r4EADer+paS^FpUi&C}>3wHAmgk>Ym;%a`~K}FhrwYg>^e${uT_E zD39!Ng@T>My?4K3`hSG~m!Su2m#Pc~U@+|_oiw30_jh&d33Logl?9+-BSWe`s3tL9 zx>L%F47B41;oLvbE3&wqe|A}4L3_7f=6Unv1tb(T<^C}%zZdcIVMY1dnJBt#%3=BK zHN-1BeviPtF1OGNM4(ilKWz{VG=|mBw?O?{m*X*Fr+MQ9-T}oYn9kFh}1=8SaVRsH5ppKQgdZL%-a{yv+ zcq&fzA87gQwBPf2pOFpmjjO_pyyr3~{Npdb&1`zjU?8Pt)*pq)n#BF{kNW}9@D9Kk zMGpVkf5#ahx&z?MNJ2lmF2YS+>DV1ITU-+bc!W;@9JKA&i%??;U`Sh0$=o;5b)?|^ zy1qq)2^$E+`lE#g`^h_iGlIirV-Y6&j7fao%kRW#>wN_<+@c;mEN1@O>puqUEw7o3 zdt1sHQ@NM;4`k_~(vo@NulNI7%~=x6YsV zZn-mXvhq><7k)^>f4uab%{w)gZZaxpsp(Z|Rzpri zE$-PuN?@ZAR;Rt(Vy$dD{(a2e*HZ38GFb){CBb(s-yc&<_z}QmP~As>lE*3fl9AfS zP4TZ(@|~oQKBR$x-RlUCzWU|qza5ab4ght;|HIKuvr=oR{tDCm1DK}QyjR3Ig81p| zuL6GJ`tzBlGz4rz_%uMjQ;EdHGww^C1dYlGlekeDIc6ook)4M}C}Ox|RdfWTe~iY+ zU5_*Du3Kl}RU$ItoFe|>c@XW>h~X80W!cQ)(D~Z*G_I}}5h}&(SB^|>;XP(SU?Gx} z_)rIDs>KCy^g#*0Hm_VV)#<&Y6X$7c>WPNnK7wDl>eTa}iN<@MDYWiE_?WA~xk?Q3 z;85F<@48gND&Ew1K4hyre((4uqLh>H@^{vK-kYv1HOBF*Q})UMUCFM6hwKKkqV%y|2@OOKyjb!h2fci1m7g{+UkVs2UG)Dwp|0ZZajjfSEVgY?f+$--`FS_FUs$|5V@Oz+8M4h%(^$t&&>CC_*|! z>;ZYeC+P6AUHZF&79#deBW+Ti28AC(!y6971r^&`2Ya|4)SFEpkLp$ql^f|0T8F0Hloom&w0tD?bAOP>cXiXn~gBUk1bP zh~WFefhcn?AhgqFXDt7_wJZXmHAFB)>yeW6(W;%YDZzHsPO#%ZY@Bv$7%R4$sYf42M^5wII1D-ZvJ;`aXi9u*+AoC z-u!=ujDjM-fz1ut|BcFig-k;5k@MN~0}<@M0pqFvA^Xn+@zY`aKSK6|4ZJ83QJG;2 zFM)!p3Ev<|Z9c%sR>OX-puLVy?D@Z)nduYnor`1pnX~KNKrRuwJ1sYNP|)Xx0=ar6Ycv{VGetHJSo)6;P`1tA|h zdSwkqR6TY^ZC}L?@(NW>Q_b8ac;;Ed=xntnn5l@11i91KcouQ{%fMD7C%7WebmMus5C*?5v|Uz-D_K1rjT z@lM*C>{lciVL>~cJy$&@WY0`CGAP)eV|A}t@G!hsH+=H~gaFwQa-s2A@4`q5N+3hS zZ+%p=aMm&}e!RG8u8Pes#k?gMrM`9f{dw=Sd=dG?w3qnrp!X#UOiWljHXA#=hBY(d zhCF5h<--{-lzGu){jS`w|jA^~m$Rh?>q1^;c%~E|jb&OJ0uZK*8uf;$4}5dEhC#XxMaPR>)b7Xg==)E&^!Qm`6bQ1 zn17=dBEl5&``SVvykrR*NG2~(dmB*uMpc)#Zf?5&A>gl#pWb{DJ1*_`K z964B;&e)EBBKl+32!)&P+M)bC!(&rdA*5y=+s`AuYft%a@+_eX@9|9Jrq%Y%5ml7gqFLtpu2`q0A$it3!vs^<(h1w}U$g9G8r^&PK;!c?eSTXH(oxNM! z?`}_|AYkWSf4v?lPhNwnU(q<-3@MDXP;K-#pb?sEs7}pWUhuj{2Xb6;T*FGqJb2sx@0f)6w z#L&1k1JPQgJ@*s)R`xg3O-85Q>rmYg71WOx_PDBXq0D;aD{Z%91H~DmHFqa_7ta{o zef#u0=)zFO-8vyNG@`xSvT8872U&z}%)PbY$`g9C&&mEU^9i^rj;3in5+wLZA|4k1 znom2roP;Q+O}%KleBrvSXFCH4M^RcjivDjHk>7 z8<|&8O9z)E=I)`e=RRJ&xW)8bUiqOSU}?s@mcmMU3Gg?pZ0n4p{2LAmDt8nU*OI*+ zl$o3`^$q8MoOsNIY8q9*{VvcX1-7V2n=$c{c%r*YUc4<2%fJjD*w2L<4%;=%B%+Hk zR_AEAE+__ulaEsmm0c&2@G!I$txiNMhs8_GmaG`(oXe_SXDhWJG(1$FuU^S8YHu@e zINVkC&wZ*7MD7SHKOi)A4%0FW^u(?QxvxhXW}}v;O0l+Iu3L!;<<5+CVul3$W(6x9 zKYw9%gSy>QGnrd=9H{z(->NKWYb3F}iVg+A=vAzRJsx-QifpNnN9yxhMz5Bxr*(u| zj~>su^H3>KOZejszXZwMRyGu0k& zJS>mAJ0*_2#!XM#%71&)W@%3HB11O(HYR| zCbM?EZ^5b+HY%xGU4TEWFWQ9)5?ljt%f5Rz6VfyVx|+91+9V7`WD*RasQy4$3ZnH! zZfxXv7{e`JjeG5=xL_n3ISxV3JqAm6s#TJ=R8sFl!s z&t~g@zY0aDxroJm*1cW_jYEGKZ41v@LYd+C!(vq_7WWJ)r+~y{UoYg8%HF!W-SAE- zlHw4hD?QYolb715B7dmkG80(WOp|>9b`?&>t+Q7f@`Alr9qL(DbR-aTyi82`EYyTR zz9rq`-m0DNSCl1Gp{};{9MxNC8}W2GiX4&bho20?ag-X`dnGpyXQ_nTQ8|(@$MC9j z?!o6&DlN~S4AL}bESMOQQ+&+v4gpseQDvek$GNOtM(9UZnRK+Zxj(5gYNv~oag8?u zwxxO-c9enI>aJ_G*Uux@yaCurKwq?KU&4hE z(Tnsi$wR`51;V;f@W#uM_9F_onXZOy7HJ*M%=8@!1U~P%*7S-Ur6_SfXV&Ih;Q|SBA@}7luje zvQmX*SGzW?$0x_roYB@cPEl`I>Z56b-zJB+fc%y9d#J5FuT|rZyUon!#CB^q!l~5= zXe&9a!QtWuVM{vo%5-e6h>;&#ZqG~>Jr+nkI4k3vX`?Q@wpulrwJu!&A<#_?Ta6q_ z{IVN1?0=wYoYG^aZ=Cp^jy!&aOr*8$0i1f^td|g#6D>nPeB?!p7__L{WNVSi0cCzj zq>0~|_aOzL-kjtSJQJVq5b`p+WA$y7*KBR=`htbOjLm|*P8P2g>ebW9%q`c5L#M~;P$z15jUC^+yU5emeJvQ4LeDyy1jt;(P2$6~4*x_tT^S(@Thr26 z<3!;gCVW@b!sNGDRQQzm^xj<^S0QuLj&_y3I_CP$D)GdHioMTT3m|>XiX9&YLT0bE zfhN7^QKpXpa%LV2C~q#W4=$d_{2Wf-knU~5v5i%m%@Be!+@VDmm)CS;-f* zqE!-NtD033{1DpgbFk-z9J;I2YQ!MCn3nnnKF*``_*I*`kJev`C%V=*c7N$9TwxBL zl{O}XBC`^O{fbrDqC7Fv4YthrJV;EkB%4N)++3=hCcx_7^(TFyJYzBG(jckTO#^5n z`8T*x%zfJKDQCZVWbXpeb{Ae+VGp5Yug4G}ifT%N4asb?WA>XM&4>;#+8dw+@Z_(Y z3LuHdUiVz9MsRR-4iw{=%N)E7!~`a1fMAD7VH0kxm_<0pmDu%BZGot+?2V8XD02kp zF4%$%KpQeU?S0=pSC&{wGS!6n3|pEivgP}8DOOu8^^9iGM`^;fseBHVr z+wzYE(9|sa92yB^XOB#>=irRmzQkpR7S8>~)xDV4VW4Q^+5$3bw=s^5aTPfu1tZoM znPuvQR@d?mQci(wgAG}W89*A3Tl<}omhunCs%3q08amZPB==K!p2oK=S9q1%ZkcfS z;-`0yQ4X-`hO3`eVv>6J6c?YqO0uBwp{hB@Qn~b_=PN7S!fhKdWY^@Z)*%wqD2ZM8 zlr5^YCX9j7N0LwiD$nzx7pOH%eWp&JVGK)F?VMXEu7rNR>47#Z6E46;dok#Et*kU0 z6jvS2$za1V*)D79+pHP1usTK9B;3JjNF$pSFH=<@eV~$O3*9$gl6N{SKvAaLIfz)L zAq!u(%1!>Bky6$IBA(USUz5Haq|dN6ik);0Z-k+G@+qsZ)qb;Dc%Z^CO3X;aGxech zE+jyS)wgr1+2g`7Zz}Lt4>ee&`ZmkGE=E=Dz*I}vtkwn&Eg)=hhB3}}jI*T0oF}n+ z@xV0|U@A7}LOt6%8-quR^+r^`p^N@GCJXSN9m3$fESJ~SfaYL?)5hmy0_6Xmn1ZjE zLZu(C{8D15tQsuVz8nh<@CTP(aYGJo9dHi}b`Ov*c-_Xu&Ev(BVtQ}4zKZMBDk-wU6 zPO^rsefU8{vuxJYBm~Qr#MRiMoNiabt8t@62ci0|C_&sIn_ zf8H1trMjJ*sqZB#md@^?z8r$Ypw}+E`1(d#g*CI0xmbSaWh&hj74?-o_8O@=O%n{Jc%TDCAV&U^gVas-9)GF+VPK zHqJ%N*^L0bRrNKX6VU2jV5lTobXo(m+eqqb<|KJ;(84Oau!G8EK44a&-VsObe!75L z@vk*Kv5?GDU=3}M=)%HowWuf&M~<7!LdoXD!Y=mKDvEzLQEQO3aO$SFiKB*rhKIP# z0G$zV5#@cfpw~@NqBaXI7P@qwSwmWS`6xA4A&{sUH-0yP*Z$;G>(+8VWtEUSTS3*7 zK@M^5{C*S39EU2P>c(7I3J*S{OUiVTmrBeEWTaC%t&M1BlELa! zj_tS~&c*l6-ggL@@9p;Q8*>iC7Gs?FTbZKSwTEya8Cx8AhK ziJG-Aun|VTBTcdDeOi{9tAsdwr-M)#0I7Wb)(hYrAq#P}z*F5qGAO*LZA@QT0c*ID;+GbF?LTO~2Z4T}Mt%Vy@i)h5Q6xAzCzR%`rkczkqq{E*hY8<7?>qnJi zuVRo>E-Z(wbVky$eK-nPwuF_L+g{t)C;moN=}Uk;l9$?|0#6O?stGH({ty=S{4VQP z^~^uWGj0SG>rVhyC^$Q~FLy_RK5x$+Svu$I4T2T;t?nxU`e6;oLMAZ$ssD#0wajI* zrZ+=1lF$!(l|zR3tLR667Agj2nboIVw5A~m6TmLB1$x4#G_d!^KbfUM8l-f{^SjTJ z0GYy90IzD4Ho+MnIs-Vmf>=YJKM*a3ut5hUa(D*5Wd7`1;AX(PnI$fmsecniShD}b zhQ&`R&*}b1zXgb6IGYWe5ARo{GD^DGB+etWwfVa4V#livO!J45zQL9*9*_COu=Q1w z3!0zdPS{KcVj5~5vk~|5&dso8_W{#ep`o+F)`6I2R_&*8({ZA0kXLgy_qA)^>b4dQ zMbB@vQl>c45X=mdLB*~8g$6kAA`IdXu!ePgcmv2-+l2$%GPFaJ9ADZk%ygD*$bXy$ zV&(L1ym)h}M|;>s5wQnDyeE$rzD%o7%sFlVNt)2v=LXWq!j}BJJR_cKHrVvuHsY-Q z0;FZ7Gv~T&L6Uq9Wb2iuN7*xq)QSFR2#~e%LcM-mVq&@!#=W=h`Dh|Ir|Du(r4f!= zgL{^?=3!W!gxcUDhgDm6VQpoIu|K$4`NmiE?Yb+AmW#A#e~Z?fN&yE2l|c}Vw1zhV zo^M|g1@Q;xH`p=)gxey};4H!a`T8!cto+ z&%tbdexAr`W#uR-EwQjroc_$=H!)c4hwstzSwl!?t3~FBSd=uDI=OAJ6saERNWx=93j7e$F^@{rx#vf z9r|zj#^+hx7{5}C^SP+LwP--zyUd)t2h^l_C3wh2{?IvBF0duA;)c}3D?A-ey3rQv z`Mr(%qdB{D)#O87(;SI2FbE6yp81f&9n<@UfO#}2r);?QWd5LgJ-#qNP(ROdZ%nr1 zVc>)sZi?T|N?h-r$x_PZ%DF(@-3F;axDIe0W-(S6CCAELAa6zgbhUBjXdw^p3D}Bp z{7J;F9F-H;g;muXU(z2If^a-2^u>k+p<)&mow5u~KZHh6 ztiAG%*=FhkGMLc;=65y{>yflx^yzjYGbJYLFUC>*QhT*((;?#S4n_Dg;F7S8?s0r; zMHr&xxn$0t1^AczRxlg*?wk7AZaZ5 zmg}W15wQT{I zm7(U!NE)&|r(d7c^9*(lB0ZT=9An2W)8y}!^V(CHl0W|OSb7DV6yRJ(g_u!V_&z5- z-k__YMLoks+n8L7rX4jU(|ttWz2H$$R>irsc9mdN)F0E0%(g=mbHZ6q#w2a&^7l?N#v!HO#Ye?wo~YZVPws&r9& z?Hg*W>6=w1n{F)>JSCfxmjVm=CWd>QtUtgs`Q26*&r9=epfcL&q1bHZ4AHn|52d*w z_imw^7YqmKu(JN}%v-MH+2`jxMb1Po3-GXScTok%fg`$2*3(KP1Yc#!Z%}OHhv9{) z&Sbzp=h&Sah?U$JdYYI9&t5sZO2-1t4oGZhI5XKs8DcH4KgL0d^+>bnmC3PUBNhOE zxf~3#6=LL0#<8m2-|0r5U8~A>5QF&?%ov~unEH}bjp6;{==v!RoorWExa9IExipbd zYQJ3r`9>5+!?YUO*arLds#R+^h+ClHRR8U)g)5R^H+P!@xRt-}#a>)z!anNqWeRYa6hMdUI;#hSYIbz5zevfc`aF@EHq}bH94C9wKmNyaUPJ$?4iF_DW#8!&C*Ly`Uc4 z4k2o{K%-1nmoy4tcs|`>j|Rn|m)Br*Z173}e7#1i4U`3^89~Mk0)`;>jXj9M;8avs z!WT;CSNNKFAgP7>T%wZgMlsm<xFnDA0T@f!`Yegh!92+%kiLu)h_L#b2D zcJw^TIm{E9URH#SK|BsB*y4MKW=}}~@8rL!G{lG#%BHTcs>x5j2kflM0h^p^Agh!V zhY*l$uPKY`{n&Y?eCVY^qE=mO<<@e-fGHN`Z#aTxGiQOYPy z2Z=}C%&C6zdDv(C&~C#q%6WKcOlY=p`Oa{w!2@0k>;R;>%#&Fv0Uz(io}e!^ZPwMl zY+wP_T=#+G$pdGoMIFaBr`|Yuw _|4|+>yEb&uv&zCQ2_Gmi{3Ym0MEFsq6*4MO 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®n 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 pub(crate) type ConnEvtChan = Arc>; pub(crate) type ConnNetChan = Arc>>>; + #[derive(Debug, Clone, Copy)] pub struct ConnMeta { /// This is important, and is stored within the server itself @@ -81,8 +82,12 @@ impl Connection { net: mpsc::Receiver>, dispatch: ConnDispatcher, ) -> Self { + todo!() } /// Initializes the client tick. - pub async fn tick() {} + pub async fn tick(&mut self) { + let sendq = self.send_queue.write().await; + // sendq.tick().await; + } } diff --git a/src/conn/queue.rs b/src/conn/queue.rs index 969977f..c8e0b87 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; use std::collections::VecDeque; +use crate::protocol::frame::FramePacket; +use crate::util::SafeGenerator; + pub enum NetQueueError { /// The insertion failed for any given reason. InvalidInsertion, @@ -163,6 +166,15 @@ pub struct TimedRecoveryQueue { queue: RecoveryQueue<(u32, Item)>, } +impl TimedRecoveryQueue { + pub fn new(max_age: u32) -> Self { + Self { + max_age, + queue: RecoveryQueue::new(), + } + } +} + /// An ordered queue is used to Index incoming packets over a channel /// within a reliable window time. /// @@ -302,9 +314,13 @@ pub struct SendQueue { /// The current sequence number. This is incremented every time /// a packet is sent reliably. We can resend these if they are - /// Acked. + /// NAcked. send_seq: u32, + /// The current fragment sequence number. This is incremented every time + /// a packet is sent reliably an sequenced. + fragment_seq: SafeGenerator, + /// The reliable packet recovery queue. /// This represents a "Sequence Index". Any socket can request /// a sequence to resend via ack. These are saved here. @@ -319,7 +335,32 @@ pub struct SendQueue { ord_queue: OrderedQueue>, } -impl SendQueue {} +impl SendQueue { + pub fn new() -> Self { + Self { + timeout: 5000, + max_tries: 5, + send_seq: 0, + fragment_seq: SafeGenerator::new(), + reliable_queue: TimedRecoveryQueue::new(10000), + unreliable_index: 0, + ord_queue: OrderedQueue::new(), + } + } + + pub fn insert(&mut self, packet: Vec, reliable: bool) -> u32 { + let next_id = self.fragment_seq.next(); + todo!() + } + + pub fn next_seq(&mut self) -> u32 { + let seq = self.send_seq; + self.send_seq = self.send_seq.wrapping_add(1); + return seq; + } +} #[derive(Debug, Clone)] -pub struct RecvQueue {} +pub struct RecvQueue { + +} diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 22c968f..ea5e77d 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,4 +1,4 @@ -use std::io::{Cursor, Write}; +use std::{io::{Cursor, Write}, collections::HashMap}; use binary_utils::error::BinaryError; use binary_utils::*; @@ -319,3 +319,31 @@ impl Streamable for Frame { Ok(stream.get_ref().clone()) } } + +// /// A smart struct that holds fragment ids bound by a packet id. +// /// This is useful for keeping track of fragment ids and reassembling them. +// /// +// /// This can be used in conjunction with an `OrderedQueue` to keep track of fragments. +// #[derive(Debug, Clone)] +// pub struct FramePool { +// /// This is a map of sequence ids to a pool of frames. +// /// The key is the sequence, and the inner vec are the frames. +// pool: HashMap>, +// /// The maximum number of fragments that can be stored in the pool. +// /// This is used to prevent the pool from growing too large. +// max_fragments: u16, +// /// The current +// } + +// impl FramePool { +// pub fn new(max_fragments: u16) -> Self { +// Self { +// pool: HashMap::new(), +// max_fragments, +// } +// } + +// pub fn next(&mut self) -> u16 { +// // gets the id that should be used next. +// } +// } \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index a0d9d03..5ad8a7a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -19,7 +19,9 @@ use crate::protocol::packet::offline::{ use crate::protocol::packet::online::Disconnect; use crate::protocol::packet::{Packet, Payload}; use crate::protocol::Magic; +use crate::rakrs_debug; use crate::server::event::ServerEventResponse; +use crate::util::to_address_token; use self::event::ServerEvent; @@ -28,6 +30,52 @@ pub type Session = ( mpsc::Sender>, mpsc::Sender<(ServerEvent, oneshot::Sender)>, ); + +// stupid hack for easier syntax :) +pub enum PossiblySocketAddr<'a> { + SocketAddr(SocketAddr), + Str(&'a str), + String(String), + ActuallyNot +} + +impl PossiblySocketAddr<'_> { + pub fn to_socket_addr(self) -> Option { + match self { + PossiblySocketAddr::SocketAddr(addr) => { + Some(addr) + }, + PossiblySocketAddr::Str(addr) => { + // we need to parse it + Some(addr.parse::().unwrap()) + }, + PossiblySocketAddr::String(addr) => { + // same as above, except less elegant >_< + Some(addr.clone().as_str().parse::().unwrap()) + }, + _ => None + } + } +} + +impl From<&str> for PossiblySocketAddr<'_> { + fn from(s: &str) -> Self { + PossiblySocketAddr::String(s.to_string()) + } +} + +impl From for PossiblySocketAddr<'_> { + fn from(s: String) -> Self { + PossiblySocketAddr::String(s) + } +} + +impl From for PossiblySocketAddr<'_> { + fn from(s: SocketAddr) -> Self { + PossiblySocketAddr::SocketAddr(s) + } +} + pub struct Listener { /// If mcpe is true, this is the default MOTD, this is /// the default MOTD to send to the client. You can change this later by setting @@ -56,12 +104,16 @@ pub struct Listener { impl Listener { /// Binds a socket to the specified addres and starts listening. - /// EG: - /// ```rust - /// let mut server = Listener::bind("0.0.0.0:19132").await; - /// let (conn, stream) = server.accept().await?.unwrap(); - /// ``` - pub async fn bind(address: SocketAddr) -> Result { + pub async fn bind Into>>(address: I) -> Result { + let a: PossiblySocketAddr = address.into(); + let address_r: Option = a.to_socket_addr(); + if address_r.is_none() { + rakrs_debug!("Invalid binding value"); + return Err(ServerError::AddrBindErr); + } + + let address = address_r.unwrap(); + let sock = match UdpSocket::bind(address).await { Ok(s) => s, Err(_) => return Err(ServerError::AddrBindErr), @@ -93,10 +145,13 @@ impl Listener { /// Starts the listener! /// ```rust - /// let mut server = Listener::bind("0.0.0.0:19132", true).await; + /// use rakrs::server::Listener; + /// async fn start() { + /// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); /// - /// // let's begin to listen to connections - /// server.start().await; + /// // let's begin to listen to connections + /// server.start().await; + /// } /// ``` pub async fn start(&mut self) -> Result<(), ServerError> { if self.serving { @@ -146,7 +201,7 @@ impl Listener { send_packet_to_socket(&socket, disconnect.into(), conn.0).await; // absolutely ensure disconnection - let (rx, rs) = oneshot::channel::(); + let (rx, _) = oneshot::channel::(); if let Err(_) = conn.1.2.send((ServerEvent::DisconnectImmediately, rx)).await { // failed to send the connection, we're gonna drop it either way } @@ -181,7 +236,9 @@ impl Listener { // notify the connection communicator if let Err(err) = send_comm.send(connection).await { + let connection = err.0; // there was an error, and we should terminate this connection immediately. + rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); sessions.remove(&origin); continue; } @@ -204,7 +261,7 @@ impl Listener { let mut motd: Motd = motd_default.clone(); let sessions = connections.lock().await; - if let Err(err) = sessions[&origin] + if let Err(_) = sessions[&origin] .2 .send(( ServerEvent::RefreshMotdRequest(origin, motd.clone()), @@ -214,6 +271,7 @@ impl Listener { { // todo event error, // we're gonna ignore it and continue by sending default motd. + rakrs_debug!(true, "[{}] Encountered an error when fetching the updated Motd.", to_address_token(*&origin)) } if let Ok(res) = resp_rx.await { @@ -248,19 +306,24 @@ impl Listener { server_id, }; + rakrs_debug!("[{}] Sent invalid RakNet protocol. Version is incompatible with server.", to_address_token(*&origin)); + send_packet_to_socket(&socket, resp.into(), origin).await; continue; } + rakrs_debug!(true, "[{}] Client requested Mtu Size: {}", to_address_token(*&origin), pk.mtu_size); + let resp = OpenConnectReply { server_id, - // todo allow encryption, find out if this is MCPE specific + // todo allow encryption security: false, magic: Magic::new(), // todo make this configurable, this is sent to the client to change // it's mtu size, right now we're using what the client prefers. // however in some cases this may not be the preferred use case, for instance - // on servers with larger worlds, you may want a larger mtu size + // on servers with larger worlds, you may want a larger mtu size, or if + // your limited on network bandwith mtu_size: pk.mtu_size, }; send_packet_to_socket(&socket, resp.into(), origin).await; @@ -286,9 +349,9 @@ impl Listener { oneshot::channel::(); sessions[&origin] .2 - .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)); + .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)).await.unwrap(); - send_packet_to_socket(&socket, resp.into(), origin); + send_packet_to_socket(&socket, resp.into(), origin).await; continue; } _ => { @@ -303,8 +366,8 @@ impl Listener { // Packet may be valid, but we'll let the connection decide this let sessions = connections.lock().await; if sessions.contains_key(&origin) { - if let Err(err) = sessions[&origin].1.send(buf[..length].to_vec()).await { - // todo log error! + if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection!", to_address_token(*&origin)); } } } @@ -355,7 +418,7 @@ async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: .send_to(&mut packet.parse().unwrap()[..], origin) .await { - // todo debug + rakrs_debug!("[{}] Failed sending payload to socket! {}", to_address_token(origin), e); } } diff --git a/src/util/debug.rs b/src/util/debug.rs new file mode 100644 index 0000000..f234329 --- /dev/null +++ b/src/util/debug.rs @@ -0,0 +1,15 @@ +/// A wrapper for println that is enabled +/// only when debug is enabled. +#[macro_export] +macro_rules! rakrs_debug { + ($heavy: ident, $($t: tt)*) => { + if cfg!(feature="debug") && cfg!(feature="debug-all") { + println!("[rakrs] DBG! {}", format!($($t)*)); + } + }; + ($($t: tt)*) => { + if cfg!(feature="debug") { + println!("[rakrs] DBG! {}", format!($($t)*)); + } + }; +} \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index a50cb63..df9140a 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,43 @@ use std::net::{SocketAddr, ToSocketAddrs}; use std::{collections::HashMap, time::SystemTime}; +pub(crate) mod debug; + +#[derive(Debug, Clone)] +pub struct SafeGenerator { + pub(crate) sequence: T, +} + +impl SafeGenerator +where + T: Default { + pub fn new() -> Self { + Self { sequence: T::default() } + } +} + +macro_rules! impl_gen { + ($n: ty) => { + impl SafeGenerator<$n> { + pub fn next(&mut self) -> $n { + self.sequence = self.sequence.wrapping_add(1); + return self.sequence; + } + + pub fn get(&self) -> $n { + self.sequence + } + } + }; +} + +impl_gen!(u8); +impl_gen!(u16); +impl_gen!(u32); +impl_gen!(u64); +impl_gen!(u128); +impl_gen!(usize); + /// This is a fancy wrapper over a HashMap that serves as /// a time oriented cache, where you can optionally clean up /// old and un-used values. Key serves as a `packet_id` in From 804acb623cfd4440dd50e4ab6af10ad5d22af96b Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 24 Dec 2022 23:35:59 -0600 Subject: [PATCH 15/75] fmt: Run formatter --- .gitignore | 1 + src/conn/queue.rs | 4 +--- src/protocol/frame.rs | 9 ++++++--- src/server/mod.rs | 33 ++++++++++++++++++++++----------- src/util/debug.rs | 2 +- src/util/mod.rs | 7 +++++-- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index bc8a328..c5ff06a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store target # Ignore lock files diff --git a/src/conn/queue.rs b/src/conn/queue.rs index c8e0b87..ab231b6 100644 --- a/src/conn/queue.rs +++ b/src/conn/queue.rs @@ -361,6 +361,4 @@ impl SendQueue { } #[derive(Debug, Clone)] -pub struct RecvQueue { - -} +pub struct RecvQueue {} diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index ea5e77d..bac9d24 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,4 +1,7 @@ -use std::{io::{Cursor, Write}, collections::HashMap}; +use std::{ + collections::HashMap, + io::{Cursor, Write}, +}; use binary_utils::error::BinaryError; use binary_utils::*; @@ -332,7 +335,7 @@ impl Streamable for Frame { // /// The maximum number of fragments that can be stored in the pool. // /// This is used to prevent the pool from growing too large. // max_fragments: u16, -// /// The current +// /// The current // } // impl FramePool { @@ -346,4 +349,4 @@ impl Streamable for Frame { // pub fn next(&mut self) -> u16 { // // gets the id that should be used next. // } -// } \ No newline at end of file +// } diff --git a/src/server/mod.rs b/src/server/mod.rs index 5ad8a7a..72226a4 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -36,24 +36,22 @@ pub enum PossiblySocketAddr<'a> { SocketAddr(SocketAddr), Str(&'a str), String(String), - ActuallyNot + ActuallyNot, } impl PossiblySocketAddr<'_> { pub fn to_socket_addr(self) -> Option { match self { - PossiblySocketAddr::SocketAddr(addr) => { - Some(addr) - }, + PossiblySocketAddr::SocketAddr(addr) => Some(addr), PossiblySocketAddr::Str(addr) => { // we need to parse it Some(addr.parse::().unwrap()) - }, + } PossiblySocketAddr::String(addr) => { // same as above, except less elegant >_< Some(addr.clone().as_str().parse::().unwrap()) - }, - _ => None + } + _ => None, } } } @@ -104,7 +102,9 @@ pub struct Listener { impl Listener { /// Binds a socket to the specified addres and starts listening. - pub async fn bind Into>>(address: I) -> Result { + pub async fn bind Into>>( + address: I, + ) -> Result { let a: PossiblySocketAddr = address.into(); let address_r: Option = a.to_socket_addr(); if address_r.is_none() { @@ -312,7 +312,12 @@ impl Listener { continue; } - rakrs_debug!(true, "[{}] Client requested Mtu Size: {}", to_address_token(*&origin), pk.mtu_size); + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {}", + to_address_token(*&origin), + pk.mtu_size + ); let resp = OpenConnectReply { server_id, @@ -349,7 +354,9 @@ impl Listener { oneshot::channel::(); sessions[&origin] .2 - .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)).await.unwrap(); + .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) + .await + .unwrap(); send_packet_to_socket(&socket, resp.into(), origin).await; continue; @@ -418,7 +425,11 @@ async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: .send_to(&mut packet.parse().unwrap()[..], origin) .await { - rakrs_debug!("[{}] Failed sending payload to socket! {}", to_address_token(origin), e); + rakrs_debug!( + "[{}] Failed sending payload to socket! {}", + to_address_token(origin), + e + ); } } diff --git a/src/util/debug.rs b/src/util/debug.rs index f234329..fc60eac 100644 --- a/src/util/debug.rs +++ b/src/util/debug.rs @@ -12,4 +12,4 @@ macro_rules! rakrs_debug { println!("[rakrs] DBG! {}", format!($($t)*)); } }; -} \ No newline at end of file +} diff --git a/src/util/mod.rs b/src/util/mod.rs index df9140a..5a52705 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -10,9 +10,12 @@ pub struct SafeGenerator { impl SafeGenerator where - T: Default { + T: Default, +{ pub fn new() -> Self { - Self { sequence: T::default() } + Self { + sequence: T::default(), + } } } From 56e9eccd0ceb5644dc074311b18fb4dd2112e41e Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 25 Dec 2022 20:24:52 -0600 Subject: [PATCH 16/75] bulk: Fix up some thing with debug and finished up connection hanshake. Only think left now is to implement Ack, Nack, and framing and we should be set --- .gitignore | 4 +- Cargo.toml | 1 + README.md | 12 +- resources/mddoc/Raknet.md | 23 ++++ resources/mddoc/reliability.md | 9 ++ src/conn/mod.rs | 93 -------------- src/connection/mod.rs | 196 ++++++++++++++++++++++++++++++ src/{conn => connection}/queue.rs | 6 + src/{conn => connection}/state.rs | 0 src/error/client.rs | 1 - src/error/connection.rs | 5 + src/error/mod.rs | 2 +- src/lib.rs | 5 +- src/protocol/ack.rs | 139 +++++++++++++++++++++ src/protocol/frame.rs | 28 ----- src/protocol/mod.rs | 4 +- src/server/event.rs | 7 +- src/server/mod.rs | 42 ++++--- src/util/debug.rs | 2 +- 19 files changed, 432 insertions(+), 147 deletions(-) delete mode 100644 src/conn/mod.rs create mode 100644 src/connection/mod.rs rename src/{conn => connection}/queue.rs (99%) rename src/{conn => connection}/state.rs (100%) delete mode 100644 src/error/client.rs create mode 100644 src/error/connection.rs create mode 100644 src/protocol/ack.rs diff --git a/.gitignore b/.gitignore index c5ff06a..bf60a69 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ target Cargo.lock *.old/ -*.profraw \ No newline at end of file +*.profraw + +raknet-test/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 8f31ca7..823d5dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" default = [ "async_tokio" ] mcpe = [] debug = [] +debug_all = [] async_std = [ "async-std" ] async_tokio = [ "tokio" ] diff --git a/README.md b/README.md index 7e5bbb3..a65388d 100644 --- a/README.md +++ b/README.md @@ -46,4 +46,14 @@ async fn main() { tokio::spawn(my_handler(conn, stream)); } } -``` \ No newline at end of file +``` + +#### Thanks to: + +- [@b24r0/rust-raknet](https://github.com/b23r0/rust-raknet)! I used this as a reference for `v3` which gave me a deeper understanding of async design patterns in rust! More specifically: + + - `Sephamore`. These are used throughout to help notify server of various signals, mainly `Close`. Sephamore's help by waiting for vairious tasks to shutdown before fully closing the listener. + + - `Notify`. These are simple by design, but they are used to synchronize actions across various threads or tasks and "wake" them up. For instance, if I want to close the server, I could notify the server to close, and the server will begin closing. + + Looking through this library also made me explore more about async rust which I probably wouldn't have done on the contrary, so a big thanks! \ No newline at end of file diff --git a/resources/mddoc/Raknet.md b/resources/mddoc/Raknet.md index e69de29..0fe3cb7 100644 --- a/resources/mddoc/Raknet.md +++ b/resources/mddoc/Raknet.md @@ -0,0 +1,23 @@ +## RakNet + +RakNet doesn't have a lot of resources on it's internal functionality, and to better understand it I've written these docs. + +RakNet is composed and derived of multiple parts, so for simplicity I've narrowed it down into the following parts: + +- [Protocol](./protocol/README.md) + + - [Connection Handshake](./protocol/handshake.md) + + - [Datagrams](./protocol/datagrams) + +- [Packet Reliability](./reliability.md) + +- [Congestion Control](./congestion.md) + +- [Plugins]() + +- [Server]() + +- [Client]() + +It's important to note that this documentation does conceptualize and take a different approach from the original RakNet implementation, however it does not deviate from the behavior of RakNet; rather providing a basis on how I believe it should be implemented. diff --git a/resources/mddoc/reliability.md b/resources/mddoc/reliability.md index e69de29..843bcad 100644 --- a/resources/mddoc/reliability.md +++ b/resources/mddoc/reliability.md @@ -0,0 +1,9 @@ +# Reliability + +Reliability within RakNet specifies when a packet should arrive and how it should be treated. The term "Reliability" within RakNet generally refers to the following possible states of a packet: + +- **Unreliable**. The packet sent is not reliable and not important. Later packets will make up for it's loss. This is the same as a regular UDP packet with the added benefit that duplicates are discarded. + +- **UnreliableSequenced**. This packet is not necessarily important, if it arrives later than another packet, you should discard it. Otherwise you can treat it normally. *This reliability is generally not used* + +- **Reliable**. The packet should be acknow diff --git a/src/conn/mod.rs b/src/conn/mod.rs deleted file mode 100644 index 7c8943d..0000000 --- a/src/conn/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -pub mod queue; -pub mod state; - -use std::{ - net::SocketAddr, - sync::{Arc, Mutex}, - time::SystemTime, -}; - -use tokio::{ - net::UdpSocket, - sync::{mpsc, oneshot, RwLock}, -}; - -use crate::server::{ - event::{ServerEvent, ServerEventResponse}, - raknet_start, -}; - -use self::queue::{RecvQueue, SendQueue}; - -pub(crate) type ConnDispatcher = - mpsc::Receiver<(ServerEvent, oneshot::Sender)>; -pub(crate) type ConnEvtChan = Arc>; -pub(crate) type ConnNetChan = Arc>>>; - -#[derive(Debug, Clone, Copy)] -pub struct ConnMeta { - /// This is important, and is stored within the server itself - /// This value is 0 until the connection state is `Connecting` - pub mtu_size: u16, - /// The time this connection last sent any data. This will be used during server tick. - pub recv_time: u64, -} - -impl ConnMeta { - pub fn new(mtu_size: u16) -> Self { - Self { - mtu_size, - recv_time: raknet_start(), - } - } -} - -/// This struct is utilized internally and represented -/// as per each "connection" or "socket" to the server. -/// This is **NOT** a struct that supports connecting TO -/// a RakNet instance, but rather a struct that HOLDS a -/// inbound connection connecting to a `Listener` instance. -/// -/// Each Connection has it's own channel for recieving -/// buffers that come from this address. -pub struct Connection { - /// The address of the connection - /// This is internally tokenized by rak-rs - pub address: SocketAddr, - - /// The queue used to send packets back to the connection. - pub(crate) send_queue: Arc>, - - /// The queue used to recieve packets, this is read from by the server. - /// This is only used internally. - pub(crate) recv_queue: Arc>, - - pub(crate) state: state::ConnectionState, - - /// The network channel, this is where the connection will be recieving it's packets. - /// If the channel is dropped, the connection is expected to drop as well, this behavior - /// has not been implemented yet. - net: ConnNetChan, - /// The event IO to communicate with the listener. - /// This is responsible for refreshing the Motd and any other overhead like, - /// raknet voice channels or plugins, however these have not been implemented yet. - dispatch: ConnEvtChan, -} - -impl Connection { - /// Initializes a new Connection instance. - pub fn new( - address: SocketAddr, - socket: &Arc, - net: mpsc::Receiver>, - dispatch: ConnDispatcher, - ) -> Self { - todo!() - } - - /// Initializes the client tick. - pub async fn tick(&mut self) { - let sendq = self.send_queue.write().await; - // sendq.tick().await; - } -} diff --git a/src/connection/mod.rs b/src/connection/mod.rs new file mode 100644 index 0000000..71d7fc4 --- /dev/null +++ b/src/connection/mod.rs @@ -0,0 +1,196 @@ +pub mod queue; +pub mod state; + +use std::{net::SocketAddr, sync::Arc, time::SystemTime}; + +use binary_utils::Streamable; +use tokio::{ + net::UdpSocket, + sync::{mpsc, oneshot, Mutex, Notify, RwLock, Semaphore}, +}; + +use crate::{ + error::connection::ConnectionError, + protocol::packet::Packet, + rakrs_debug, + server::{ + event::{ServerEvent, ServerEventResponse}, + raknet_start, + }, + util::to_address_token, +}; + +use self::queue::{RecvQueue, SendQueue}; + +pub(crate) type ConnDispatcher = + mpsc::Receiver<(ServerEvent, oneshot::Sender)>; +pub(crate) type ConnEvtChan = Arc>; +pub(crate) type ConnNetChan = Arc>>>; + +#[derive(Debug, Clone, Copy)] +pub struct ConnMeta { + /// This is important, and is stored within the server itself + /// This value is 0 until the connection state is `Connecting` + pub mtu_size: u16, + /// The time this connection last sent any data. This will be used during server tick. + pub recv_time: u64, +} + +impl ConnMeta { + pub fn new(mtu_size: u16) -> Self { + Self { + mtu_size, + recv_time: raknet_start(), + } + } +} + +/// This struct is utilized internally and represented +/// as per each "connection" or "socket" to the server. +/// This is **NOT** a struct that supports connecting TO +/// a RakNet instance, but rather a struct that HOLDS a +/// inbound connection connecting to a `Listener` instance. +/// +/// Each Connection has it's own channel for recieving +/// buffers that come from this address. +pub struct Connection { + /// The address of the connection + /// This is internally tokenized by rak-rs + pub address: SocketAddr, + /// The queue used to send packets back to the connection. + pub(crate) send_queue: Arc>, + /// The queue used to recieve packets, this is read from by the server. + /// This is only used internally. + pub(crate) recv_queue: Arc>, + pub(crate) state: state::ConnectionState, + /// The network channel, this is where the connection will be recieving it's packets. + /// This is interfaced to provide the api for `Connection::recv()` + internal_net_recv: ConnNetChan, + /// The event IO to communicate with the listener. + /// This is responsible for refreshing the Motd and any other overhead like, + /// raknet voice channels or plugins, however these have not been implemented yet. + dispatch: ConnEvtChan, + /// A notifier for when the connection should close. + /// This is used for absolute cleanup withtin the connection + disconnect: Arc, + /// The last time a packet was recieved. This is used to keep the connection from + /// being in memory longer than it should be. + recv_time: Arc>, +} + +impl Connection { + /// Initializes a new Connection instance. + pub async fn new( + address: SocketAddr, + socket: &Arc, + net: mpsc::Receiver>, + dispatch: ConnDispatcher, + ) -> Self { + let (net_sender, net_receiver) = mpsc::channel::>(100); + let c = Self { + address, + send_queue: Arc::new(RwLock::new(SendQueue::new())), + recv_queue: Arc::new(Mutex::new(RecvQueue::new())), + internal_net_recv: Arc::new(Mutex::new(net_receiver)), + dispatch: Arc::new(Mutex::new(dispatch)), + state: state::ConnectionState::Unidentified, + disconnect: Arc::new(Semaphore::new(0)), + recv_time: Arc::new(Mutex::new(SystemTime::now())), + }; + + c.init_tick(socket).await; + c.init_net_recv(socket, net, net_sender).await; + + return c; + } + + /// Initializes the client ticking process! + pub async fn init_tick(&self, socket: &Arc) { + // initialize the event io + // we initialize the ticking function here, it's purpose is to update the state of the current connection + // while handling throttle + } + + /// This function initializes the raw internal packet handling task! + /// + pub async fn init_net_recv( + &self, + socket: &Arc, + mut net: mpsc::Receiver>, + sender: mpsc::Sender>, + ) { + let recv_time = self.recv_time.clone(); + let recv_q = self.recv_queue.clone(); + let send_q = self.send_queue.clone(); + let disconnect = self.disconnect.clone(); + let state = self.state; + let address = self.address; + + tokio::spawn(async move { + loop { + if disconnect.is_closed() { + rakrs_debug!( + "[{}] Network reciever task disbanding!", + to_address_token(address) + ); + break; + } + + if let Some(payload) = net.recv().await { + // We've recieved a payload! + drop(*recv_time.lock().await = SystemTime::now()); + + // Validate this packet + // this is a raw buffer! + // There's a few things that need to be done here. + // 1. Validate the type of packet + // 2. Determine how the packet should be processed + // 3. If the packet is assembled and should be sent to the client + // send it to the communication channel using `sender` + rakrs_debug!( + true, + "[{}] Unknown RakNet packet recieved.", + to_address_token(address) + ); + } + } + }); + } + + /// Handle a RakNet Event. These are sent as they happen. + /// + /// EG: + /// ```ignore + /// let conn: Connection = Connection::new(); + /// + /// while let Some((event, responder)) = conn.recv_ev { + /// match event { + /// ServerEvent::SetMtuSize(mtu) => { + /// println!("client updated mtu!"); + /// responder.send(ServerEventResponse::Acknowledge); + /// } + /// } + /// } + /// ``` + pub async fn recv_ev( + &self, + ) -> Result<(ServerEvent, oneshot::Sender), ConnectionError> { + match self.dispatch.lock().await.recv().await { + Some((server_event, event_responder)) => { + return Ok((server_event, event_responder)); + } + None => { + if self.disconnect.is_closed() { + return Err(ConnectionError::Closed); + } + return Err(ConnectionError::EventDispatchError); + } + } + } + + /// Initializes the client tick. + pub async fn tick(&mut self) { + let sendq = self.send_queue.write().await; + // sendq.tick().await; + } +} diff --git a/src/conn/queue.rs b/src/connection/queue.rs similarity index 99% rename from src/conn/queue.rs rename to src/connection/queue.rs index ab231b6..650bffa 100644 --- a/src/conn/queue.rs +++ b/src/connection/queue.rs @@ -362,3 +362,9 @@ impl SendQueue { #[derive(Debug, Clone)] pub struct RecvQueue {} + +impl RecvQueue { + pub fn new() -> Self { + Self {} + } +} diff --git a/src/conn/state.rs b/src/connection/state.rs similarity index 100% rename from src/conn/state.rs rename to src/connection/state.rs diff --git a/src/error/client.rs b/src/error/client.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/error/client.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/error/connection.rs b/src/error/connection.rs new file mode 100644 index 0000000..5543865 --- /dev/null +++ b/src/error/connection.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy)] +pub enum ConnectionError { + Closed, + EventDispatchError, +} diff --git a/src/error/mod.rs b/src/error/mod.rs index c07f47e..f63bcdd 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,2 +1,2 @@ -pub mod client; +pub mod connection; pub mod server; diff --git a/src/lib.rs b/src/lib.rs index 4f39506..ef67fc7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ -pub mod conn; +pub mod connection; pub mod error; pub mod protocol; pub mod server; pub mod util; + +pub use protocol::mcpe::{self, motd::Motd}; +pub use server::Listener; diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs new file mode 100644 index 0000000..2ab4131 --- /dev/null +++ b/src/protocol/ack.rs @@ -0,0 +1,139 @@ +use std::{io::Cursor, ops::Range}; + +use binary_utils::Streamable; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; + +/// An ack record. +/// A record holds a single or range of acked packets. +/// No real complexity other than that. +#[derive(Debug, Clone)] +pub enum Record { + Single(SingleRecord), + Range(RangeRecord), +} + +#[derive(Debug, Clone)] +pub struct SingleRecord { + pub sequence: u32, +} + +#[derive(Debug, Clone)] +pub struct RangeRecord { + pub start: u32, + pub end: u32, +} + +impl RangeRecord { + /// Fixes the end of the range if it is lower than the start. + pub fn fix(&mut self) { + if self.end < self.start { + let temp = self.end; + self.end = self.start; + self.start = temp; + } + } +} + +#[derive(Debug, Clone)] +pub struct Ack { + pub id: u8, + pub count: u16, + pub records: Vec, +} + +impl Ack { + pub fn new(count: u16, nack: bool) -> Self { + Self { + id: if nack { 0xa0 } else { 0xc0 }, + count, + records: Vec::new(), + } + } + + pub fn push_record(&mut self, seq: u32) { + self.records + .push(Record::Single(SingleRecord { sequence: seq })); + } + + pub fn from_missing(missing: Vec) -> Self { + let mut records: Vec = Vec::new(); + let mut current: Range = 0..0; + + for m in missing { + if current.end + 1 == m { + current.end += 1; + } else if m > current.end { + // This is a new range. + records.push(Record::Range(RangeRecord { + start: current.start, + end: current.end, + })); + current.start = m; + current.end = m; + } else { + // This is a new single. + records.push(Record::Single(SingleRecord { sequence: m })); + current.start = m + 1; + current.end = m + 1; + } + } + + let mut nack = Self::new(records.len().try_into().unwrap(), true); + nack.records = records; + + return nack; + } +} + +impl Streamable for Ack { + fn parse(&self) -> Result, binary_utils::error::BinaryError> { + let mut stream: Vec = Vec::new(); + stream.push(self.id); + stream.write_u16::(self.count)?; + + for record in self.records.iter() { + match record { + Record::Single(rec) => { + stream.push(1); + stream.write_u24::(rec.sequence)?; + } + Record::Range(rec) => { + stream.push(0); + stream.write_u24::(rec.start)?; + stream.write_u24::(rec.end)?; + } + } + } + Ok(stream) + } + + fn compose( + source: &[u8], + position: &mut usize, + ) -> Result { + let mut stream = Cursor::new(source); + let id = stream.read_u8().unwrap(); + let count = stream.read_u16::().unwrap(); + let mut records: Vec = Vec::new(); + for _ in 0..count { + if stream.read_u8().unwrap() == 1 { + let record: SingleRecord = SingleRecord { + sequence: stream.read_u24::().unwrap(), + }; + + records.push(Record::Single(record)); + } else { + let record: RangeRecord = RangeRecord { + start: stream.read_u24::().unwrap(), + end: stream.read_u24::().unwrap(), + }; + + records.push(Record::Range(record)); + } + } + + *position += stream.position() as usize; + + Ok(Self { count, records, id }) + } +} \ No newline at end of file diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index bac9d24..01cf335 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -322,31 +322,3 @@ impl Streamable for Frame { Ok(stream.get_ref().clone()) } } - -// /// A smart struct that holds fragment ids bound by a packet id. -// /// This is useful for keeping track of fragment ids and reassembling them. -// /// -// /// This can be used in conjunction with an `OrderedQueue` to keep track of fragments. -// #[derive(Debug, Clone)] -// pub struct FramePool { -// /// This is a map of sequence ids to a pool of frames. -// /// The key is the sequence, and the inner vec are the frames. -// pool: HashMap>, -// /// The maximum number of fragments that can be stored in the pool. -// /// This is used to prevent the pool from growing too large. -// max_fragments: u16, -// /// The current -// } - -// impl FramePool { -// pub fn new(max_fragments: u16) -> Self { -// Self { -// pool: HashMap::new(), -// max_fragments, -// } -// } - -// pub fn next(&mut self) -> u16 { -// // gets the id that should be used next. -// } -// } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 4ce206e..0a5c850 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod frame; pub(crate) mod magic; -pub(crate) mod mcpe; -pub(crate) mod packet; +pub mod mcpe; +pub mod packet; pub mod reliability; pub use magic::*; diff --git a/src/server/event.rs b/src/server/event.rs index 6a4ee7e..e1695b2 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -1,6 +1,6 @@ use std::net::SocketAddr; -use crate::{conn::state::ConnectionState, protocol::mcpe::motd::Motd}; +use crate::{connection::state::ConnectionState, protocol::mcpe::motd::Motd}; #[derive(Debug, Clone)] pub enum ServerEvent { @@ -27,4 +27,9 @@ pub enum ServerEvent { pub enum ServerEventResponse { /// The response to a `RefreshMotdRequest`. RefreshMotd(Motd), + /// A generic response that acknowledges the event was recieved, but + /// no actions were taken. + /// + /// VALID FOR ALL EVENTS + Acknowledged, } diff --git a/src/server/mod.rs b/src/server/mod.rs index 72226a4..2ed322a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -10,7 +10,7 @@ use tokio::net::UdpSocket; use tokio::sync::{mpsc, oneshot}; use tokio::sync::{Mutex, Notify, Semaphore}; -use crate::conn::{ConnMeta, Connection}; +use crate::connection::{ConnMeta, Connection}; use crate::error::server::ServerError; use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ @@ -122,9 +122,10 @@ impl Listener { let server_id: u64 = rand::random(); let motd = Motd::new(server_id, format!("{}", address.port())); - // The buffer is 100 here because locking on large servers may cause - // locking to take longer, and this accounts for that. - let (send_comm, recv_comm) = mpsc::channel::(100); + // This channel is a Communication channel for when `Connection` structs are initialized. + let (send_comm, recv_comm) = mpsc::channel::(10); + // This channel is responsible for handling and dispatching events between clients. + // Oneshot will garauntee this event is intended for the client whom requested the event. let (send_evnt, recv_evnt) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); @@ -166,6 +167,8 @@ impl Listener { let closer = self.closer.clone(); let cleanup = self.cleanup.clone(); + self.serving = true; + tokio::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; @@ -212,7 +215,6 @@ impl Listener { break; } - // todo disconnect notification }; // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally @@ -228,7 +230,7 @@ impl Listener { ServerEvent, oneshot::Sender, )>(10); - let connection = Connection::new(origin, &socket, net_recv, evt_recv); + let connection = Connection::new(origin, &socket, net_recv, evt_recv).await; // Add the connection to the available connections list. // we're using the name "sessions" here to differeniate @@ -271,17 +273,19 @@ impl Listener { { // todo event error, // we're gonna ignore it and continue by sending default motd. - rakrs_debug!(true, "[{}] Encountered an error when fetching the updated Motd.", to_address_token(*&origin)) + rakrs_debug!("[{}] Encountered an error when dispatching ServerEvent::RefreshMotdRequest.", to_address_token(*&origin)) } if let Ok(res) = resp_rx.await { - // this was a motd event, - // lets send the pong! - // get the motd from the server event otherwise use defaults. match res { - ServerEventResponse::RefreshMotd(m) => motd = m, - _ => {} + ServerEventResponse::RefreshMotd(m) => { + rakrs_debug!(true, "[{}] Motd set by ServerEventResponse::RefreshMotd.", to_address_token(origin)); + motd = m; + } + _ => { + rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); + } }; } @@ -350,13 +354,17 @@ impl Listener { sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size, ); - let (resp_tx, resp_rx) = - oneshot::channel::(); - sessions[&origin] + let (resp_tx, _) = oneshot::channel::(); + if let Err(_) = sessions[&origin] .2 .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) .await - .unwrap(); + { + rakrs_debug!( + "[{}] Failed to update mtu size with the client!", + to_address_token(origin) + ); + } send_packet_to_socket(&socket, resp.into(), origin).await; continue; @@ -374,7 +382,7 @@ impl Listener { let sessions = connections.lock().await; if sessions.contains_key(&origin) { if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { - rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection!", to_address_token(*&origin)); + rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed!", to_address_token(*&origin)); } } } diff --git a/src/util/debug.rs b/src/util/debug.rs index fc60eac..2a3a4c4 100644 --- a/src/util/debug.rs +++ b/src/util/debug.rs @@ -3,7 +3,7 @@ #[macro_export] macro_rules! rakrs_debug { ($heavy: ident, $($t: tt)*) => { - if cfg!(feature="debug") && cfg!(feature="debug-all") { + if cfg!(feature="debug") && cfg!(feature="debug_all") { println!("[rakrs] DBG! {}", format!($($t)*)); } }; From 67cb7693e368fc8910051ec45de48f5264864ab1 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 1 Jan 2023 11:07:00 -0600 Subject: [PATCH 17/75] chore: update readme --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index a65388d..7e5bbb3 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,4 @@ async fn main() { tokio::spawn(my_handler(conn, stream)); } } -``` - -#### Thanks to: - -- [@b24r0/rust-raknet](https://github.com/b23r0/rust-raknet)! I used this as a reference for `v3` which gave me a deeper understanding of async design patterns in rust! More specifically: - - - `Sephamore`. These are used throughout to help notify server of various signals, mainly `Close`. Sephamore's help by waiting for vairious tasks to shutdown before fully closing the listener. - - - `Notify`. These are simple by design, but they are used to synchronize actions across various threads or tasks and "wake" them up. For instance, if I want to close the server, I could notify the server to close, and the server will begin closing. - - Looking through this library also made me explore more about async rust which I probably wouldn't have done on the contrary, so a big thanks! \ No newline at end of file +``` \ No newline at end of file From 5b4b3e140403a2a4821873845e4d6d32277011b4 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 1 Jan 2023 11:08:46 -0600 Subject: [PATCH 18/75] chore: update readme --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7e5bbb3..7ebc7fa 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,10 @@ async fn my_handler(conn: RakConnection, mut stream: RakStream) { async fn main() { // Bind to a socket and allow minecraft protocol let mut server = Listener::bind("0.0.0.0:19132", true).await; - server.motd = Motd::new( - "Rust Bedrock Minecraft server", - // 100 players, maximum - 100, - // The minecraft version to display - "1.18.0", - // The Gamemode to display - mcpe::Gamemode::Creative - ); + server.motd.name = "Rust Bedrock Minecraft server"; + server.motd.player_count = 100; + server.motd.player_max = 200; + server.motd.gamemode = mcpe::Gamemode::Survival; // Begin listening to incoming connections server.start().await; From 66f916822af47caea0620eace444215cc3d2ec81 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 3 Jan 2023 01:27:43 -0600 Subject: [PATCH 19/75] bulk: Finishing up v3 feat: Add queues feat: Add reliable windows feat: Finish recv queue feat: Fix fragmentation --- resources/mddoc/protocol/README.md | 1 + src/connection/controller/mod.rs | 6 + src/connection/controller/window.rs | 86 ++++++ src/connection/mod.rs | 180 ++++++++++- src/connection/queue.rs | 370 ----------------------- src/connection/queue/mod.rs | 451 ++++++++++++++++++++++++++++ src/connection/queue/recv.rs | 108 +++++++ src/connection/queue/send.rs | 104 +++++++ src/protocol/ack.rs | 22 +- src/protocol/mcpe/motd.rs | 8 +- src/protocol/mod.rs | 4 + src/protocol/packet/offline.rs | 4 +- src/protocol/reliability.rs | 3 + src/server/mod.rs | 13 +- 14 files changed, 962 insertions(+), 398 deletions(-) create mode 100644 src/connection/controller/mod.rs create mode 100644 src/connection/controller/window.rs delete mode 100644 src/connection/queue.rs create mode 100644 src/connection/queue/mod.rs create mode 100644 src/connection/queue/recv.rs create mode 100644 src/connection/queue/send.rs diff --git a/resources/mddoc/protocol/README.md b/resources/mddoc/protocol/README.md index e69de29..bdd2b07 100644 --- a/resources/mddoc/protocol/README.md +++ b/resources/mddoc/protocol/README.md @@ -0,0 +1 @@ +# RakNet Protocol diff --git a/src/connection/controller/mod.rs b/src/connection/controller/mod.rs new file mode 100644 index 0000000..18d4aa7 --- /dev/null +++ b/src/connection/controller/mod.rs @@ -0,0 +1,6 @@ +// todo +pub mod window; + +pub struct Controller { + pub window: window::ReliableWindow, +} diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs new file mode 100644 index 0000000..2299e2a --- /dev/null +++ b/src/connection/controller/window.rs @@ -0,0 +1,86 @@ +use std::collections::HashMap; + +use crate::server::current_epoch; + +#[derive(Debug, Clone)] +pub struct ReliableWindow { + // The current window start and end + window: (u32, u32), + // The current window size + size: u32, + // the current queue of packets by timestamp + queue: HashMap, +} + +impl ReliableWindow { + pub fn new() -> Self { + Self { + window: (0, 2048), + size: 2048, + queue: HashMap::new(), + } + } + + pub fn insert(&mut self, index: u32) -> bool { + // We already got this packet + if index < self.window.0 || index > self.window.1 || self.queue.contains_key(&index) { + return false; + } + + self.queue.insert(index, current_epoch()); + + // we need to update the window to check if the is within it. + if index == self.window.0 { + self.adjust(); + } + + return true; + } + + /// Attempts to adjust the window size, removing all out of date packets + /// from the queue. + pub fn adjust(&mut self) { + // remove all packets that are out of date, that we got before the window, + // increasing the window start and end if we can. + while self.queue.contains_key(&self.window.0) { + self.queue.remove(&self.window.0); + self.window.0 = self.window.0.wrapping_add(1); + self.window.1 = self.window.1.wrapping_add(1); + } + + // if the window is too small or too big, make sure it's the right size. + // corresponding to self.size + let curr_size = self.window.1.wrapping_sub(self.window.0); + if curr_size < self.size { + self.window.1 = self.window.0.wrapping_add(self.size); + } else if curr_size > self.size { + self.window.0 = self.window.1.wrapping_sub(self.size); + } + } + + /// Returns all the packets that are in the window. + pub fn missing(&mut self) -> Vec { + let mut missing = Vec::new(); + + for i in self.window.0..self.window.1 { + if !self.queue.contains_key(&i) { + missing.push(i); + } + } + + missing + } + + /// Forcefully clears packets that are not in the window. + /// This is used when the window is too small to fit all the packets. + pub fn clear_outdated(&mut self) { + //todo actually clear packets that are out of date! + self.queue + .retain(|k, _| *k >= self.window.0 && *k <= self.window.1); + } +} + +pub struct Window { + // last round trip time + pub rtt: u32, +} diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 71d7fc4..1c095da 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -1,3 +1,5 @@ +pub mod controller; +/// Necessary queues for the connection. pub mod queue; pub mod state; @@ -6,21 +8,32 @@ use std::{net::SocketAddr, sync::Arc, time::SystemTime}; use binary_utils::Streamable; use tokio::{ net::UdpSocket, - sync::{mpsc, oneshot, Mutex, Notify, RwLock, Semaphore}, + sync::{ + mpsc::{self, Sender}, + oneshot, Mutex, Notify, RwLock, Semaphore, + }, }; use crate::{ error::connection::ConnectionError, - protocol::packet::Packet, + protocol::{ + ack::{Ack, Ackable, ACK, NACK}, + frame::FramePacket, + packet::{online::OnlinePacket, Packet}, + }, rakrs_debug, server::{ + current_epoch, event::{ServerEvent, ServerEventResponse}, - raknet_start, }, util::to_address_token, }; -use self::queue::{RecvQueue, SendQueue}; +use self::{ + controller::window::ReliableWindow, + queue::{RecvQueue, SendQueue}, + state::ConnectionState, +}; pub(crate) type ConnDispatcher = mpsc::Receiver<(ServerEvent, oneshot::Sender)>; @@ -40,7 +53,7 @@ impl ConnMeta { pub fn new(mtu_size: u16) -> Self { Self { mtu_size, - recv_time: raknet_start(), + recv_time: current_epoch(), } } } @@ -57,12 +70,12 @@ pub struct Connection { /// The address of the connection /// This is internally tokenized by rak-rs pub address: SocketAddr, + pub(crate) state: Arc>, /// The queue used to send packets back to the connection. - pub(crate) send_queue: Arc>, + send_queue: Arc>, /// The queue used to recieve packets, this is read from by the server. /// This is only used internally. - pub(crate) recv_queue: Arc>, - pub(crate) state: state::ConnectionState, + recv_queue: Arc>, /// The network channel, this is where the connection will be recieving it's packets. /// This is interfaced to provide the api for `Connection::recv()` internal_net_recv: ConnNetChan, @@ -93,7 +106,7 @@ impl Connection { recv_queue: Arc::new(Mutex::new(RecvQueue::new())), internal_net_recv: Arc::new(Mutex::new(net_receiver)), dispatch: Arc::new(Mutex::new(dispatch)), - state: state::ConnectionState::Unidentified, + state: Arc::new(Mutex::new(ConnectionState::Unidentified)), disconnect: Arc::new(Semaphore::new(0)), recv_time: Arc::new(Mutex::new(SystemTime::now())), }; @@ -101,6 +114,10 @@ impl Connection { c.init_tick(socket).await; c.init_net_recv(socket, net, net_sender).await; + // todo finish the send queue + // todo finish the ticking function + // todo add function for user to accept and send packets! + return c; } @@ -123,14 +140,15 @@ impl Connection { let recv_q = self.recv_queue.clone(); let send_q = self.send_queue.clone(); let disconnect = self.disconnect.clone(); - let state = self.state; + let state = self.state.clone(); let address = self.address; tokio::spawn(async move { loop { if disconnect.is_closed() { rakrs_debug!( - "[{}] Network reciever task disbanding!", + true, + "[{}] Recv task has been closed!", to_address_token(address) ); break; @@ -147,16 +165,146 @@ impl Connection { // 2. Determine how the packet should be processed // 3. If the packet is assembled and should be sent to the client // send it to the communication channel using `sender` - rakrs_debug!( - true, - "[{}] Unknown RakNet packet recieved.", - to_address_token(address) - ); + + let id = payload[0]; + match id { + // This is a frame packet. + // This packet will be handled by the recv_queue + 0x80..=0x8d => { + if let Ok(pk) = FramePacket::compose(&payload[..], &mut 0) { + let mut rq = recv_q.lock().await; + + if let Ok(_) = rq.insert(pk) {}; + + let buffers = rq.flush(); + + for buffer in buffers { + let res = Connection::process_packet( + &buffer, &address, &sender, &send_q, &state, + ) + .await; + if let Ok(v) = res { + if v == true { + // DISCONNECT + disconnect.close(); + break; + } + } + if let Err(_) = res { + rakrs_debug!( + true, + "[{}] Failed to process packet!", + to_address_token(address) + ); + }; + } + + drop(rq); + } + } + NACK => { + // Validate this is a nack packet + if let Ok(nack) = Ack::compose(&payload[..], &mut 0) { + // The client acknowledges it did not recieve these packets + // We should resend them. + let mut sq = send_q.write().await; + let resend = sq.nack(nack); + for packet in resend { + if let Err(_) = sender.send(packet).await { + rakrs_debug!( + true, + "[{}] Failed to send packet to client!", + to_address_token(address) + ); + } + } + } + } + ACK => { + // first lets validate this is an ack packet + if let Ok(ack) = Ack::compose(&payload[..], &mut 0) { + // The client acknowledges it recieved these packets + // We should remove them from the queue. + let mut sq = send_q.write().await; + sq.ack(ack); + } + } + _ => { + rakrs_debug!( + true, + "[{}] Unknown RakNet packet recieved (Or packet is sent out of scope).", + to_address_token(address) + ); + } + }; } } }); } + pub async fn process_packet( + buffer: &Vec, + address: &SocketAddr, + sender: &Sender>, + send_q: &Arc>, + state: &Arc>, + ) -> Result { + if let Ok(packet) = Packet::compose(buffer, &mut 0) { + if packet.is_online() { + return match packet.get_online() { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: SystemTime::now() + .duration_since(connection.start_time) + .unwrap() + .as_millis() as i64, + }; + Ok(true) + } + OnlinePacket::ConnectionRequest(pk) => { + let response = ConnectionAccept { + system_index: 0, + client_address: from_address_token(connection.address.clone()), + internal_id: SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), + 19132, + ), + request_time: pk.time, + timestamp: SystemTime::now() + .duration_since(connection.start_time) + .unwrap() + .as_millis() as i64, + }; + Ok(true) + } + OnlinePacket::Disconnect(_) => { + // Disconnect the client immediately. + connection.disconnect("Client disconnected.", false); + Ok(false) + } + OnlinePacket::NewConnection(_) => { + connection.state = ConnectionState::Connected; + Ok(true) + } + _ => { + sender.send(buffer.clone()).await.unwrap(); + Ok(true) + } + }; + } else { + *state.lock().await = ConnectionState::Disconnecting; + rakrs_debug!( + true, + "[{}] Invalid protocol! Disconnecting client!", + to_address_token(*address) + ); + return Err(()); + } + } + Err(()) + } + /// Handle a RakNet Event. These are sent as they happen. /// /// EG: diff --git a/src/connection/queue.rs b/src/connection/queue.rs deleted file mode 100644 index 650bffa..0000000 --- a/src/connection/queue.rs +++ /dev/null @@ -1,370 +0,0 @@ -use std::collections::HashMap; -use std::collections::VecDeque; - -use crate::protocol::frame::FramePacket; -use crate::util::SafeGenerator; - -pub enum NetQueueError { - /// The insertion failed for any given reason. - InvalidInsertion, - /// The insertion failed and the reason is known. - InvalidInsertionKnown(String), - /// The `Item` failed to be removed from the queue. - ItemDeletionFail, - Other(E), -} - -pub trait NetQueue { - /// The `Item` of the queue. - // type Item = V; - - /// The "key" that each `Item` is stored under - /// (used for removal) - type KeyId; - - /// A custom error specifier for NetQueueError - type Error; - - /// Inserts `Item` into the queue, given the conditions are fulfilled. - fn insert(&mut self, item: Item) -> Result>; - - /// Remove an `Item` from the queue by providing an instance of `Self::KeyId` - fn remove(&mut self, key: Self::KeyId) -> Result>; - - /// Retrieves an `Item` from the queue, by reference. - fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError>; - - /// Clears the entire queue. - fn flush(&mut self) -> Result, NetQueueError>; -} - -/// A specialized struct that will keep records of `T` -/// up to a certain capacity specified with `RecoveryQueue::with_capacity(u32)` -/// during construction. This means any records that are hold, are dropped off and forgotten. -/// -/// By default the recovery queue -/// will store `255` records of `T`. -/// -/// The maximum records allowed are `u32::MAX`, however not -/// advised. -/// -/// ```rust -/// use rakrs::conn::queue::RecoveryQueue; -/// -/// // Create a new recovery queue, of u8 -/// let mut queue = RecoveryQueue::::new(); -/// let indexes = ( -/// // 0 -/// queue.insert(1), -/// // 1 -/// queue.insert(4), -/// // 2 -/// queue.insert(6) -/// ); -/// -/// queue.recover(1); // Result<0> -/// queue.recover(2); // Result<6> -/// queue.get(1); // Result<4> -/// -/// assert_eq!(queue.recover(1), Ok(4)); -/// assert_eq!(queue.get(1), Ok(4)); -/// assert_eq!(queue.get(4), Err()); -/// ``` -#[derive(Debug, Clone)] -pub struct RecoveryQueue { - /// (index, resend_attempts, Item) - recovery: VecDeque<(u32, Item)>, - capacity: u32, - index: u32, -} - -impl RecoveryQueue { - pub fn new() -> Self { - Self { - recovery: VecDeque::with_capacity(255), - capacity: 255, - index: 0, - } - } - - pub fn with_capacity(capacity: u32) -> Self { - Self { - recovery: VecDeque::with_capacity(capacity.try_into().unwrap()), - capacity, - index: 0, - } - } - - /// Set the capacity of the recovery queue. - /// This may be called by raknet if a load of clients - /// start trying to connect or if the I/O of network - /// begins writing more than reading. - pub fn set_capacity(&mut self, factor: u32) -> bool { - // todo: IF factor is greator than self.capacity, replace - // todo: the current capacity with a vector of that capacity. - if factor > 0 { - self.capacity = factor; - self.validate_capacity(); - true - } else { - false - } - } - - /// Add a new item into the recovery queue. - /// If the item addition exceeds the current - /// capacity of the queue, the queue is shifted. - /// - /// This method does not validate for duplicates, - /// for that, use `new_insert` - pub fn insert(&mut self, item: Item) -> u32 { - self.validate_capacity(); - - let idx = self.index; - self.recovery.push_back((idx, item)); - self.index += 1; - - return idx; - } - - /// Validates that adding a new entry will not exceed - /// the capacity of the queue itself. - fn validate_capacity(&mut self) { - while self.recovery.len() >= self.capacity as usize { - self.recovery.pop_front(); - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -pub enum RecoveryQueueError { - /// The index given is not valid, either because it is from the - /// future, or overflows. - Invalid, - /// The index given is not recoverable, but was cached earlier, - /// you should not try to retrieve this index again. - IndexOld, - /// The insertion failed because the Item is already recoverable. - /// - /// **This is only enforced if used with `insert_new`** - Duplicate, -} - -/// A Record of `Item` where each `Item` may only live for `max_age`. -/// If an `Item` exceeds the duration of `max_age`, it is dropped. -/// -/// This structure is **NOT** ticked, meaning any records are stale -/// until the structure is interacted with. -#[derive(Clone, Debug)] -pub struct TimedRecoveryQueue { - /// The maximum age a packet is allowed to live in the queue for. - /// If the age is exceeded, the item will be dropped. - pub max_age: u32, - /// A private recovery queue, this will hold our (`Time`, Item) - /// We will then be able to clear out old packets by "Time", or if - /// the capacity of the queue is reached. - queue: RecoveryQueue<(u32, Item)>, -} - -impl TimedRecoveryQueue { - pub fn new(max_age: u32) -> Self { - Self { - max_age, - queue: RecoveryQueue::new(), - } - } -} - -/// An ordered queue is used to Index incoming packets over a channel -/// within a reliable window time. -/// -/// Usage: -/// ```rust -/// use rakrs::conn::queue::OrderedQueue; -/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); -/// // Insert a packet with the id of "1" -/// ord_qu.insert(vec![0, 1], 1); -/// ord_qu.insert(vec![1, 0], 5); -/// ord_qu.insert(vec![2, 0], 3); -/// -/// // Get the packets we still need. -/// let needed: Vec = ord_qu.flush_missing(); -/// assert_eq!(needed, vec![0, 2, 4]); -/// -/// // We would in theory, request these packets, but we're going to insert them -/// ord_qu.insert(vec![2, 0, 0, 1], 4); -/// ord_qu.insert(vec![1, 0, 0, 2], 2); -/// -/// // Now let's return our packets in order. -/// // Will return a vector of these packets in order by their "id". -/// let ordered: Vec> = ord_qu.flush(); -/// ``` -#[derive(Debug)] -pub struct OrderedQueue { - /// The queue of packets that are in order. Mapped to the time they were received. - queue: HashMap, - /// The current starting scope for the queue. - /// A start scope or "window start" is the range of packets that we are currently allowing. - /// Older packets will be ignored simply because they are old. - scope: (u32, u32), -} - -impl Clone for OrderedQueue -where - T: Clone, -{ - fn clone(&self) -> Self { - OrderedQueue { - queue: self.queue.clone(), - scope: self.scope.clone(), - } - } -} - -impl OrderedQueue -where - T: Sized + Clone, -{ - pub fn new() -> Self { - Self { - queue: HashMap::new(), - scope: (0, 0), - } - } - - /// Inserts the given packet into the queue. - /// This will return `false` if the packet is out of scope. - pub fn insert(&mut self, packet: T, id: u32) -> bool { - // if the packet id is lower than our scope, ignore it - // this packet is way to old for us to handle. - if id < self.scope.0 { - return false; - } - - // If the packet is higher than our current scope, we need to adjust our scope. - // This is because we are now allowing packets that are newer than our current scope. - if id > self.scope.1 { - self.scope.1 = id + 1; - } - - self.queue.insert(id, packet); - return true; - } - - /// Drains the current queue by removing all packets from the queue. - /// This will return the packets in order only if they were within the current scope. - /// This method will also update the scope and adjust it to the newest window. - pub fn flush(&mut self) -> Vec { - // clear all packets not within our scope - self.clear_out_of_scope(); - - // now drain the queue - let mut map = HashMap::new(); - std::mem::swap(&mut map, &mut self.queue); - - let mut clean = map.iter().collect::>(); - clean.sort_by_key(|m| m.0); - - return clean.iter().map(|m| m.1.clone()).collect::>(); - } - - /// Clears all packets that are out of scope. - /// Returning only the ones that have not been recieved. - pub fn flush_missing(&mut self) -> Vec { - let mut missing: Vec = Vec::new(); - // we need to get the amount of ids that are missing from the queue. - for i in self.scope.0..self.scope.1 { - if !self.queue.contains_key(&i) { - missing.push(i); - } - } - - // we can safely update the scope - self.scope.0 = missing.get(0).unwrap_or(&self.scope.0).clone(); - return missing; - } - - fn clear_out_of_scope(&mut self) { - // clear all packets not within our current scope. - // this is done by removing all packets that are older than our current scope. - for (id, _) in self.queue.clone().iter() { - if *id < self.scope.0 { - self.queue.remove(id); - } - } - } - - pub fn get_scope(&self) -> u32 { - self.scope.1 - self.scope.0 - } -} - -/// This queue is used to prioritize packets being sent out -/// Packets that are old, are either dropped or requested again. -/// You can define this behavior with the `timeout` property. -#[derive(Debug, Clone)] -pub struct SendQueue { - /// The amount of time that needs to pass for a packet to be - /// dropped or requested again. - timeout: u16, - - /// The amount of times we should retry sending a packet before - /// dropping it from the queue. This is currently set to `5`. - max_tries: u16, - - /// The current sequence number. This is incremented every time - /// a packet is sent reliably. We can resend these if they are - /// NAcked. - send_seq: u32, - - /// The current fragment sequence number. This is incremented every time - /// a packet is sent reliably an sequenced. - fragment_seq: SafeGenerator, - - /// The reliable packet recovery queue. - /// This represents a "Sequence Index". Any socket can request - /// a sequence to resend via ack. These are saved here. - reliable_queue: TimedRecoveryQueue>, - - /// The unreliable packet sequence count. - unreliable_index: u32, - - /// This is a special queue nested within the send queue. It will - /// automatically clean up packets that "are out of scope" or - /// "outside the window" - ord_queue: OrderedQueue>, -} - -impl SendQueue { - pub fn new() -> Self { - Self { - timeout: 5000, - max_tries: 5, - send_seq: 0, - fragment_seq: SafeGenerator::new(), - reliable_queue: TimedRecoveryQueue::new(10000), - unreliable_index: 0, - ord_queue: OrderedQueue::new(), - } - } - - pub fn insert(&mut self, packet: Vec, reliable: bool) -> u32 { - let next_id = self.fragment_seq.next(); - todo!() - } - - pub fn next_seq(&mut self) -> u32 { - let seq = self.send_seq; - self.send_seq = self.send_seq.wrapping_add(1); - return seq; - } -} - -#[derive(Debug, Clone)] -pub struct RecvQueue {} - -impl RecvQueue { - pub fn new() -> Self { - Self {} - } -} diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs new file mode 100644 index 0000000..7a557cc --- /dev/null +++ b/src/connection/queue/mod.rs @@ -0,0 +1,451 @@ +pub(crate) mod recv; +pub(crate) mod send; + +pub use self::recv::*; +pub use self::send::*; + +use std::collections::HashMap; + +use crate::protocol::frame::FragmentMeta; +use crate::protocol::frame::Frame; +use crate::server::current_epoch; + +pub enum NetQueueError { + /// The insertion failed for any given reason. + InvalidInsertion, + /// The insertion failed and the reason is known. + InvalidInsertionKnown(String), + /// The `Item` failed to be removed from the queue. + ItemDeletionFail, + /// The `Item` is invalid and can not be retrieved. + InvalidItem, + /// The queue is empty. + EmptyQueue, + /// The error is a custom error. + Other(E), +} + +pub trait NetQueue { + /// The `Item` of the queue. + // type Item = V; + + /// The "key" that each `Item` is stored under + /// (used for removal) + type KeyId; + + /// A custom error specifier for NetQueueError + type Error; + + /// Inserts `Item` into the queue, given the conditions are fulfilled. + fn insert(&mut self, item: Item) -> Result>; + + /// Remove an `Item` from the queue by providing an instance of `Self::KeyId` + fn remove(&mut self, key: Self::KeyId) -> Result>; + + /// Retrieves an `Item` from the queue, by reference. + fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError>; + + /// Clears the entire queue. + fn flush(&mut self) -> Result, NetQueueError>; +} + +/// A recovery queue is used to store packets that need to be resent. +/// This is used for sequenced and ordered packets. +#[derive(Debug, Clone)] +pub struct RecoveryQueue { + /// The current queue of packets by timestamp + /// (seq, (packet, timestamp)) + /// todo use the timestamp for round trip time (RTT) + queue: HashMap, +} + +impl RecoveryQueue { + pub fn new() -> Self { + Self { + queue: HashMap::new(), + } + } +} + +impl NetQueue for RecoveryQueue { + type KeyId = u32; + type Error = (); + + fn insert(&mut self, item: Item) -> Result> { + let index = self.queue.len() as u32; + self.queue.insert(index, (current_epoch(), item)); + Ok(index) + } + + fn remove(&mut self, key: Self::KeyId) -> Result> { + if let Some((_, item)) = self.queue.remove(&key) { + Ok(item) + } else { + Err(NetQueueError::ItemDeletionFail) + } + } + + fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError> { + if let Some((_, item)) = self.queue.get(&key) { + Ok(item) + } else { + Err(NetQueueError::ItemDeletionFail) + } + } + + fn flush(&mut self) -> Result, NetQueueError> { + let mut items = Vec::new(); + for (_, (_, item)) in self.queue.drain() { + items.push(item); + } + Ok(items) + } +} + +#[derive(Debug, Clone)] +pub struct OrderedQueue { + /// The current ordered queue channels + /// Channel, (Highest Index, Ord Index, Item) + pub queue: HashMap, + /// The window for this queue. + pub window: (u32, u32), +} + +impl OrderedQueue +where + Item: Clone + std::fmt::Debug, +{ + pub fn new() -> Self { + Self { + queue: HashMap::new(), + window: (0, 0), + } + } + + pub fn insert(&mut self, index: u32, item: Item) -> bool { + if index < self.window.0 { + return false; + } + + if self.queue.contains_key(&index) { + return false; + } + + if index >= self.window.1 { + self.window.1 = index + 1; + } + + self.queue.insert(index, item); + true + } + + pub fn flush(&mut self) -> Vec { + let mut items = Vec::::new(); + while self.queue.contains_key(&self.window.0) { + if let Some(item) = self.queue.remove(&self.window.0) { + items.push(item); + } else { + break; + } + self.window.0 = self.window.0.wrapping_add(1); + } + + return items; + } +} + +/// An ordered queue is used to Index incoming packets over a channel +/// within a reliable window time. +/// +/// Usage: +/// ```rust +/// use rakrs::conn::queue::OrderedQueue; +/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); +/// // Insert a packet with the id of "1" +/// ord_qu.insert(vec![0, 1], 1); +/// ord_qu.insert(vec![1, 0], 5); +/// ord_qu.insert(vec![2, 0], 3); +/// +/// // Get the packets we still need. +/// let needed: Vec = ord_qu.flush_missing(); +/// assert_eq!(needed, vec![0, 2, 4]); +/// +/// // We would in theory, request these packets, but we're going to insert them +/// ord_qu.insert(vec![2, 0, 0, 1], 4); +/// ord_qu.insert(vec![1, 0, 0, 2], 2); +/// +/// // Now let's return our packets in order. +/// // Will return a vector of these packets in order by their "id". +/// let ordered: Vec> = ord_qu.flush(); +/// ``` +#[derive(Debug)] +pub struct OrderedQueueOld { + /// The queue of packets that are in order. Mapped to the time they were received. + queue: HashMap, + /// The current starting scope for the queue. + /// A start scope or "window start" is the range of packets that we are currently allowing. + /// Older packets will be ignored simply because they are old. + scope: (u32, u32), +} + +impl Clone for OrderedQueueOld +where + T: Clone, +{ + fn clone(&self) -> Self { + OrderedQueueOld { + queue: self.queue.clone(), + scope: self.scope.clone(), + } + } +} + +impl OrderedQueueOld +where + T: Sized + Clone, +{ + pub fn new() -> Self { + Self { + queue: HashMap::new(), + scope: (0, 0), + } + } + + /// Inserts the given packet into the queue. + /// This will return `false` if the packet is out of scope. + pub fn insert(&mut self, packet: T, id: u32) -> bool { + // if the packet id is lower than our scope, ignore it + // this packet is way to old for us to handle. + if id < self.scope.0 { + return false; + } + + // If the packet is higher than our current scope, we need to adjust our scope. + // This is because we are now allowing packets that are newer than our current scope. + if id > self.scope.1 { + self.scope.1 = id + 1; + } + + self.queue.insert(id, packet); + return true; + } + + /// Drains the current queue by removing all packets from the queue. + /// This will return the packets in order only if they were within the current scope. + /// This method will also update the scope and adjust it to the newest window. + pub fn flush(&mut self) -> Vec { + // clear all packets not within our scope + self.clear_out_of_scope(); + + // now drain the queue + let mut map = HashMap::new(); + std::mem::swap(&mut map, &mut self.queue); + + let mut clean = map.iter().collect::>(); + clean.sort_by_key(|m| m.0); + + return clean.iter().map(|m| m.1.clone()).collect::>(); + } + + /// Clears all packets that are out of scope. + /// Returning only the ones that have not been recieved. + pub fn flush_missing(&mut self) -> Vec { + let mut missing: Vec = Vec::new(); + // we need to get the amount of ids that are missing from the queue. + for i in self.scope.0..self.scope.1 { + if !self.queue.contains_key(&i) { + missing.push(i); + } + } + + // we can safely update the scope + self.scope.0 = missing.get(0).unwrap_or(&self.scope.0).clone(); + return missing; + } + + fn clear_out_of_scope(&mut self) { + // clear all packets not within our current scope. + // this is done by removing all packets that are older than our current scope. + for (id, _) in self.queue.clone().iter() { + if *id < self.scope.0 { + self.queue.remove(id); + } + } + } + + pub fn get_scope(&self) -> u32 { + self.scope.1 - self.scope.0 + } +} + +/// A specialized structure for re-ordering fragments over the wire. +/// You can use this structure to fragment frames as well. +/// +/// **NOTE:** This structure will NOT update a frame's reliable index! +/// The sender is required to this! +#[derive(Clone, Debug)] +pub struct FragmentQueue { + /// The current fragment id to use + /// If for some reason this wraps back to 0, + /// and the fragment queue is full, 0 is then cleared and reused. + fragment_id: u16, + + /// The current Fragments + /// Hashmap is by Fragment id, with the value being + /// (`size`, Vec) + fragments: HashMap)>, +} + +impl FragmentQueue { + pub fn new() -> Self { + Self { + fragment_id: 0, + fragments: HashMap::new(), + } + } + + /// Inserts the frame into the fragment queue. + /// Returns a result tuple of (`fragment_size`, `fragment_index`) + pub fn insert(&mut self, fragment: Frame) -> Result<(u32, u32), FragmentQueueError> { + if let Some(meta) = fragment.fragment_meta.clone() { + if let Some((size, frames)) = self.fragments.get_mut(&meta.id) { + // check if the frame index is out of bounds + if meta.index >= *size { + return Err(FragmentQueueError::FrameIndexOutOfBounds); + } + // the frame exists, and we have parts, check if we have this particular frame already. + if let Some(_) = frames + .iter() + .find(|&f| f.fragment_meta.as_ref().unwrap().index == meta.index) + { + // We already have this frame! Do not replace it!! + return Err(FragmentQueueError::FrameExists); + } else { + frames.push(fragment); + return Ok((meta.size, meta.index)); + } + } else { + // We don't already have this fragment index! + let (size, mut frames) = (meta.size, Vec::::new()); + frames.push(fragment); + + self.fragments.insert(meta.id, (size, frames)); + return Ok((meta.size, meta.index)); + } + } + + return Err(FragmentQueueError::FrameNotFragmented); + } + + /// Attempts to collect all fragments from a given fragment id. + /// Will fail if not all fragments are specified. + pub fn collect(&mut self, id: u16) -> Result, FragmentQueueError> { + if let Some((size, frames)) = self.fragments.get_mut(&id) { + if *size == frames.len() as u32 { + // we have all the frames! + frames.sort_by(|a, b| { + a.fragment_meta + .as_ref() + .unwrap() + .id + .cmp(&b.fragment_meta.as_ref().unwrap().id) + }); + + let mut buffer = Vec::::new(); + + for frame in frames.iter() { + buffer.extend_from_slice(&frame.body); + } + + self.fragments.remove(&id); + return Ok(buffer); + } + return Err(FragmentQueueError::FragmentsMissing); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + /// This will split a given frame into a bunch of smaller frames within the specified + /// restriction. + pub fn split_insert(&mut self, frame: Frame, mtu: u16) -> Result { + let max_mtu = mtu - 60; + + if frame.body.len() > max_mtu.into() { + let splits = frame + .body + .chunks(max_mtu.into()) + .map(|c| c.to_vec()) + .collect::>>(); + let id = self.fragment_id.wrapping_add(1); + let mut index = 0; + let mut frames: Vec = Vec::new(); + + for buf in splits.iter() { + let mut f = Frame::init(); + f.fragment_meta = Some(FragmentMeta { + index, + size: splits.len() as u32, + id, + }); + + f.reliability = frame.reliability; + f.flags = frame.flags; + f.size = buf.len() as u16; + f.body = buf.clone(); + f.order_index = frame.order_index; + f.order_channel = frame.order_channel; + f.reliable_index = frame.reliable_index; + + index += 1; + + frames.push(f); + } + + if self.fragments.contains_key(&id) { + self.fragments.remove(&id); + } + + self.fragments.insert(id, (splits.len() as u32, frames)); + return Ok(id); + } + + return Err(FragmentQueueError::DoesNotNeedSplit); + } + + pub fn get(&self, id: &u16) -> Result<&(u32, Vec), FragmentQueueError> { + if let Some(v) = self.fragments.get(id) { + return Ok(v); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + pub fn get_mut(&mut self, id: &u16) -> Result<&mut (u32, Vec), FragmentQueueError> { + if let Some(v) = self.fragments.get_mut(id) { + return Ok(v); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + pub fn remove(&mut self, id: &u16) -> bool { + self.fragments.remove(id).is_some() + } + + /// This will hard clear the fragment queue, this should only be used if memory becomes an issue! + pub fn clear(&mut self) { + self.fragment_id = 0; + self.fragments.clear(); + } +} + +#[derive(Debug, Clone, Copy)] +pub enum FragmentQueueError { + FrameExists, + FrameNotFragmented, + DoesNotNeedSplit, + FragmentInvalid, + FragmentsMissing, + FrameIndexOutOfBounds, +} diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs new file mode 100644 index 0000000..19bd0ba --- /dev/null +++ b/src/connection/queue/recv.rs @@ -0,0 +1,108 @@ +use binary_utils::*; +use std::collections::{HashMap, HashSet}; + +use crate::connection::controller::window::ReliableWindow; +use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; +use crate::protocol::frame::{Frame, FramePacket}; +use crate::protocol::reliability::Reliability; +use crate::protocol::MAX_FRAGS; +use crate::rakrs_debug; +use crate::server::current_epoch; + +use super::{FragmentQueue, OrderedQueue}; + +#[derive(Debug, Clone)] +pub enum RecvQueueError { + OldSeq, +} + +#[derive(Debug, Clone)] +pub struct RecvQueue { + frag_queue: FragmentQueue, + window: ReliableWindow, + reliable_window: ReliableWindow, + order_channels: HashMap>>, + /// Set of sequences that we've acknowledged. + /// (seq, time) + ack: HashSet<(u32, u64)>, + ready: Vec>, +} + +impl RecvQueue { + pub fn new() -> Self { + Self { + frag_queue: FragmentQueue::new(), + ack: HashSet::new(), + window: ReliableWindow::new(), + reliable_window: ReliableWindow::new(), + ready: Vec::new(), + order_channels: HashMap::new(), + } + } + + pub fn insert(&mut self, packet: FramePacket) -> Result<(), RecvQueueError> { + if !self.window.insert(packet.sequence) { + return Err(RecvQueueError::OldSeq); + } + + self.ack.insert((packet.sequence, current_epoch())); + + for frame in packet.frames.iter() { + self.handle_frame(frame); + } + + return Ok(()); + } + + pub fn flush(&mut self) -> Vec> { + self.ready.drain(..).collect::>>() + } + + fn handle_frame(&mut self, frame: &Frame) { + if let Some(reliable_index) = frame.reliable_index { + if !self.reliable_window.insert(reliable_index) { + return; + } + } + if let Some(meta) = frame.fragment_meta.as_ref() { + if meta.size > MAX_FRAGS { + rakrs_debug!(true, "Fragment size is too large, rejected {}!", meta.size); + return; + } + if let Err(_) = self.frag_queue.insert(frame.clone()) {} + + let res = self.frag_queue.collect(meta.id); + if let Ok(data) = res { + // reconstructed frame packet! + self.ready.push(data); + } + return; + } + match frame.reliability { + Reliability::Unreliable => { + self.ready.push(frame.body.clone()); + } + Reliability::Reliable => { + self.ready.push(frame.body.clone()); + } + Reliability::ReliableOrd => { + let channel = frame.order_channel.unwrap(); + let queue = self + .order_channels + .entry(channel) + .or_insert(OrderedQueue::new()); + + if !queue.insert(frame.order_index.unwrap(), frame.body.clone()) { + return; + } + + for pk in queue.flush() { + self.ready.push(pk); + } + } + _ => { + self.ready.push(frame.body.clone()); + } + } + } +} diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs new file mode 100644 index 0000000..6287c91 --- /dev/null +++ b/src/connection/queue/send.rs @@ -0,0 +1,104 @@ +use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; +use crate::protocol::frame::FramePacket; +use crate::util::SafeGenerator; + +use super::{NetQueue, RecoveryQueue}; + +/// This queue is used to prioritize packets being sent out +/// Packets that are old, are either dropped or requested again. +/// You can define this behavior with the `timeout` property. +#[derive(Debug, Clone)] +pub struct SendQueue { + /// The amount of time that needs to pass for a packet to be + /// dropped or requested again. + timeout: u16, + + /// The amount of times we should retry sending a packet before + /// dropping it from the queue. This is currently set to `5`. + max_tries: u16, + + /// The current sequence number. This is incremented every time + /// a packet is sent reliably. We can resend these if they are + /// NAcked. + send_seq: u32, + + /// The current reliable index number. + /// a packet is sent reliably an sequenced. + reliable_seq: SafeGenerator, + + /// The current recovery queue. + ack: RecoveryQueue>, +} + +impl SendQueue { + pub fn new() -> Self { + Self { + timeout: 5000, + max_tries: 5, + send_seq: 0, + reliable_seq: SafeGenerator::new(), + ack: RecoveryQueue::new(), + } + } + + pub fn insert(&mut self, packet: Vec, reliable: bool) -> u32 { + // let next_id = self.fragment_seq.next(); + todo!() + } + + pub fn next_seq(&mut self) -> u32 { + let seq = self.send_seq; + self.send_seq = self.send_seq.wrapping_add(1); + return seq; + } +} + +impl Ackable for SendQueue { + fn ack(&mut self, ack: Ack) { + if ack.is_nack() { + return; + } + + // these packets are acknowledged, so we can remove them from the queue. + for record in ack.records.iter() { + match record { + Record::Single(SingleRecord { sequence }) => { + self.ack.remove(*sequence); + } + Record::Range(ranged) => { + for i in ranged.start..ranged.end { + self.ack.remove(i); + } + } + } + } + } + + fn nack(&mut self, nack: Ack) -> Vec> { + if !nack.is_nack() { + return Vec::new(); + } + + let mut resend_queue = Vec::>::new(); + + // we need to get the packets to resend. + for record in nack.records.iter() { + match record { + Record::Single(single) => { + if let Ok(packet) = self.ack.get(single.sequence) { + resend_queue.push(packet.clone()); + } + } + Record::Range(ranged) => { + for i in ranged.start..ranged.end { + if let Ok(packet) = self.ack.get(i) { + resend_queue.push(packet.clone()); + } + } + } + } + } + + return resend_queue; + } +} diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 2ab4131..2a1cd51 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -1,8 +1,24 @@ +pub const ACK: u8 = 0xc0; +pub const NACK: u8 = 0xa0; + use std::{io::Cursor, ops::Range}; use binary_utils::Streamable; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; +pub(crate) trait Ackable { + /// When an ack packet is recieved. + /// We should ack the queue + fn ack(&mut self, index: Ack) {} + + /// When an NACK packet is recieved. + /// We should nack the queue + /// This should return the packets that need to be resent. + fn nack(&mut self, packet: Ack) -> Vec> { + todo!() + } +} + /// An ack record. /// A record holds a single or range of acked packets. /// No real complexity other than that. @@ -50,6 +66,10 @@ impl Ack { } } + pub fn is_nack(&self) -> bool { + self.id == 0xa0 + } + pub fn push_record(&mut self, seq: u32) { self.records .push(Record::Single(SingleRecord { sequence: seq })); @@ -136,4 +156,4 @@ impl Streamable for Ack { Ok(Self { count, records, id }) } -} \ No newline at end of file +} diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index 36d0f4a..0833401 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -43,9 +43,9 @@ pub struct Motd { /// The version of the server pub version: String, /// The number of players online - pub player_count: u16, + pub player_count: u32, /// The maximum number of players - pub player_max: u16, + pub player_max: u32, /// The gamemode of the server pub gamemode: Gamemode, /// The server's GUID @@ -172,11 +172,11 @@ impl Streamable for Motd { version: version.clone(), player_count: player_count .as_str() - .parse::() + .parse::() .expect("Player count is not a number"), player_max: player_max .as_str() - .parse::() + .parse::() .expect("Player Maximum is not a number"), server_guid: server_guid .as_str() diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 0a5c850..10f4c87 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod ack; pub(crate) mod frame; pub(crate) mod magic; pub mod mcpe; @@ -5,3 +6,6 @@ pub mod packet; pub mod reliability; pub use magic::*; + +pub const MAX_FRAGS: u32 = 1024; +pub const MAX_ORD_CHANS: u8 = 32; diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index ce85bd6..3510d58 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -64,8 +64,8 @@ packet_id!(UnconnectedPong, 0x1c); #[derive(Debug, Clone)] pub struct OpenConnectRequest { pub magic: Magic, - pub protocol: u8, - pub mtu_size: u16, + pub protocol: u8, // 9 + pub mtu_size: u16, // 500 } impl Streamable for OpenConnectRequest { fn compose(source: &[u8], position: &mut usize) -> Result { diff --git a/src/protocol/reliability.rs b/src/protocol/reliability.rs index c548cb9..0c611bd 100644 --- a/src/protocol/reliability.rs +++ b/src/protocol/reliability.rs @@ -10,8 +10,11 @@ pub enum Reliability { ReliableOrd, /// Reliably sequenced **AND** ordered ReliableSeq, + /// never used over the wire UnreliableAck, + /// never used over the wire ReliableAck, + /// never used over the wire ReliableOrdAck, } diff --git a/src/server/mod.rs b/src/server/mod.rs index 2ed322a..96b5495 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -81,6 +81,8 @@ pub struct Listener { pub motd: Motd, /// A server Id, passed in unconnected pong. pub id: u64, + /// Supported versions + pub versions: &'static [u8], /// Whether or not the server is being served. serving: bool, /// The current socket. @@ -132,6 +134,7 @@ impl Listener { let listener = Self { sock: Some(Arc::new(sock)), id: server_id, + versions: &[10, 11], motd, send_comm, recv_comm, @@ -166,6 +169,7 @@ impl Listener { let connections = self.connections.clone(); let closer = self.closer.clone(); let cleanup = self.cleanup.clone(); + let versions = self.versions.clone(); self.serving = true; @@ -280,7 +284,6 @@ impl Listener { // get the motd from the server event otherwise use defaults. match res { ServerEventResponse::RefreshMotd(m) => { - rakrs_debug!(true, "[{}] Motd set by ServerEventResponse::RefreshMotd.", to_address_token(origin)); motd = m; } _ => { @@ -291,7 +294,7 @@ impl Listener { // unconnected pong signature is different if MCPE is specified. let resp = UnconnectedPong { - timestamp: raknet_start(), + timestamp: current_epoch(), server_id, magic: Magic::new(), #[cfg(feature = "mcpe")] @@ -303,14 +306,14 @@ impl Listener { } OfflinePacket::OpenConnectRequest(pk) => { // todo make a constant for this - if pk.protocol != 10_u8 { + if !versions.contains(&pk.protocol) { let resp = IncompatibleProtocolVersion { protocol: pk.protocol, magic: Magic::new(), server_id, }; - rakrs_debug!("[{}] Sent invalid RakNet protocol. Version is incompatible with server.", to_address_token(*&origin)); + rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); send_packet_to_socket(&socket, resp.into(), origin).await; continue; @@ -441,7 +444,7 @@ async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: } } -pub(crate) fn raknet_start() -> u64 { +pub(crate) fn current_epoch() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() From a63fdbfdd4f1bddfd56736e1efac592b8404173e Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 19:51:23 -0600 Subject: [PATCH 20/75] feat: Async-std --- Cargo.toml | 4 +- src/connection/mod.rs | 231 ++++++++++++++++------------ src/connection/queue/mod.rs | 65 +++++--- src/connection/queue/send.rs | 102 ++++++++++--- src/protocol/ack.rs | 4 +- src/protocol/frame.rs | 19 +++ src/protocol/mod.rs | 5 + src/server/mod.rs | 281 +++++++++++++++++++---------------- src/util/mod.rs | 9 ++ 9 files changed, 451 insertions(+), 269 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 823d5dc..09a6e64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Bavfalcon9 "] edition = "2021" [features] -default = [ "async_tokio" ] +default = [ "async-std" ] mcpe = [] debug = [] debug_all = [] @@ -19,4 +19,4 @@ tokio = { version = "1.15.0", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" futures-executor = "0.3.19" -async-std = { version = "1.10.0", optional = true } +async-std = { version = "1.10.0", optional = true, features = [ "unstable" ] } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 1c095da..dd1aa08 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -3,15 +3,27 @@ pub mod controller; pub mod queue; pub mod state; -use std::{net::SocketAddr, sync::Arc, time::SystemTime}; +use std::{net::SocketAddr, sync::{Arc, atomic::{AtomicU64, AtomicBool}}, time::{SystemTime, Duration}}; +use async_std::{channel::{self, bounded}, task::JoinHandle}; use binary_utils::Streamable; +#[cfg(feature = "async-std")] +use async_std::{ + net::UdpSocket, + channel::{ + Sender, Receiver + }, + sync::{ + Condvar, Mutex, RwLock, + }, task::sleep, +}; +#[cfg(feature = "tokio")] use tokio::{ net::UdpSocket, sync::{ mpsc::{self, Sender}, oneshot, Mutex, Notify, RwLock, Semaphore, - }, + }, time::{timeout, sleep}, }; use crate::{ @@ -19,7 +31,7 @@ use crate::{ protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, - packet::{online::OnlinePacket, Packet}, + packet::{online::{OnlinePacket, ConnectedPong}, Packet}, }, rakrs_debug, server::{ @@ -34,11 +46,7 @@ use self::{ queue::{RecvQueue, SendQueue}, state::ConnectionState, }; - -pub(crate) type ConnDispatcher = - mpsc::Receiver<(ServerEvent, oneshot::Sender)>; -pub(crate) type ConnEvtChan = Arc>; -pub(crate) type ConnNetChan = Arc>>>; +pub(crate) type ConnNetChan = Arc>>>; #[derive(Debug, Clone, Copy)] pub struct ConnMeta { @@ -79,16 +87,17 @@ pub struct Connection { /// The network channel, this is where the connection will be recieving it's packets. /// This is interfaced to provide the api for `Connection::recv()` internal_net_recv: ConnNetChan, - /// The event IO to communicate with the listener. - /// This is responsible for refreshing the Motd and any other overhead like, - /// raknet voice channels or plugins, however these have not been implemented yet. - dispatch: ConnEvtChan, /// A notifier for when the connection should close. /// This is used for absolute cleanup withtin the connection - disconnect: Arc, + disconnect: Arc, + /// The event dispatcher for the connection. + // evt_sender: Sender<(ServerEvent, oneshot::Sender)>, + /// The event receiver for the connection. + // evt_receiver: mpsc::Receiver<(ServerEvent, oneshot::Sender)>, /// The last time a packet was recieved. This is used to keep the connection from /// being in memory longer than it should be. - recv_time: Arc>, + recv_time: Arc, + tasks: Arc>>>, } impl Connection { @@ -96,23 +105,29 @@ impl Connection { pub async fn new( address: SocketAddr, socket: &Arc, - net: mpsc::Receiver>, - dispatch: ConnDispatcher, + net: channel::Receiver>, + mtu: u16, ) -> Self { - let (net_sender, net_receiver) = mpsc::channel::>(100); + let (net_sender, net_receiver) = bounded::>(100); + // let (evt_sender, evt_receiver) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); let c = Self { address, - send_queue: Arc::new(RwLock::new(SendQueue::new())), + send_queue: Arc::new(RwLock::new(SendQueue::new(mtu, 12000, 5, socket.clone(), address))), recv_queue: Arc::new(Mutex::new(RecvQueue::new())), internal_net_recv: Arc::new(Mutex::new(net_receiver)), - dispatch: Arc::new(Mutex::new(dispatch)), + // evt_sender, + // evt_receiver, state: Arc::new(Mutex::new(ConnectionState::Unidentified)), - disconnect: Arc::new(Semaphore::new(0)), - recv_time: Arc::new(Mutex::new(SystemTime::now())), + // disconnect: Arc::new(Condvar::new()), + disconnect: Arc::new(AtomicBool::new(false)), + recv_time: Arc::new(AtomicU64::new(current_epoch())), + tasks: Arc::new(Mutex::new(Vec::new())), }; - c.init_tick(socket).await; - c.init_net_recv(socket, net, net_sender).await; + let tk = c.tasks.clone(); + let mut tasks = tk.lock().await; + tasks.push(c.init_tick(socket)); + tasks.push(c.init_net_recv(socket, net, net_sender).await); // todo finish the send queue // todo finish the ticking function @@ -122,10 +137,30 @@ impl Connection { } /// Initializes the client ticking process! - pub async fn init_tick(&self, socket: &Arc) { + pub fn init_tick(&self, socket: &Arc) -> async_std::task::JoinHandle<()>{ + let address = self.address; + let closer = self.disconnect.clone(); + let last_recv = self.recv_time.clone(); + // initialize the event io // we initialize the ticking function here, it's purpose is to update the state of the current connection // while handling throttle + return async_std::task::spawn(async move { + loop { + sleep(Duration::from_millis(50)).await; + + if last_recv.load(std::sync::atomic::Ordering::Relaxed) + 20 >= current_epoch() { + rakrs_debug!( + true, + "[{}] Connection has been closed due to inactivity!", + to_address_token(address) + ); + // closer.notify_all(); + closer.store(true, std::sync::atomic::Ordering::Relaxed); + break; + } + } + }); } /// This function initializes the raw internal packet handling task! @@ -133,9 +168,9 @@ impl Connection { pub async fn init_net_recv( &self, socket: &Arc, - mut net: mpsc::Receiver>, - sender: mpsc::Sender>, - ) { + mut net: Receiver>, + sender: Sender>, + ) -> async_std::task::JoinHandle<()> { let recv_time = self.recv_time.clone(); let recv_q = self.recv_queue.clone(); let send_q = self.send_queue.clone(); @@ -143,9 +178,9 @@ impl Connection { let state = self.state.clone(); let address = self.address; - tokio::spawn(async move { + return async_std::task::spawn(async move { loop { - if disconnect.is_closed() { + if disconnect.load(std::sync::atomic::Ordering::Relaxed) { rakrs_debug!( true, "[{}] Recv task has been closed!", @@ -154,17 +189,9 @@ impl Connection { break; } - if let Some(payload) = net.recv().await { + if let Ok(payload) = net.recv().await { // We've recieved a payload! - drop(*recv_time.lock().await = SystemTime::now()); - - // Validate this packet - // this is a raw buffer! - // There's a few things that need to be done here. - // 1. Validate the type of packet - // 2. Determine how the packet should be processed - // 3. If the packet is assembled and should be sent to the client - // send it to the communication channel using `sender` + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); let id = payload[0]; match id { @@ -186,7 +213,8 @@ impl Connection { if let Ok(v) = res { if v == true { // DISCONNECT - disconnect.close(); + // disconnect.close(); + disconnect.store(true, std::sync::atomic::Ordering::Relaxed); break; } } @@ -210,10 +238,18 @@ impl Connection { let mut sq = send_q.write().await; let resend = sq.nack(nack); for packet in resend { - if let Err(_) = sender.send(packet).await { + if let Ok(buffer) = packet.parse() { + if let Err(_) = sender.send(buffer).await { + rakrs_debug!( + true, + "[{}] Failed to send packet to client!", + to_address_token(address) + ); + } + } else { rakrs_debug!( true, - "[{}] Failed to send packet to client!", + "[{}] Failed to send packet to client (parsing failed)!", to_address_token(address) ); } @@ -251,40 +287,40 @@ impl Connection { ) -> Result { if let Ok(packet) = Packet::compose(buffer, &mut 0) { if packet.is_online() { + println!("Recieved packet: {:?}", packet); return match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { ping_time: pk.time, - pong_time: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, + pong_time: current_epoch() as i64 }; - Ok(true) + let q = send_q.write().await; + // q.insert_immediate(response); + Ok(false) } OnlinePacket::ConnectionRequest(pk) => { - let response = ConnectionAccept { - system_index: 0, - client_address: from_address_token(connection.address.clone()), - internal_id: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), - 19132, - ), - request_time: pk.time, - timestamp: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, - }; + // let response = ConnectionAccept { + // system_index: 0, + // client_address: from_address_token(connection.address.clone()), + // internal_id: SocketAddr::new( + // IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), + // 19132, + // ), + // request_time: pk.time, + // timestamp: SystemTime::now() + // .duration_since(connection.start_time) + // .unwrap() + // .as_millis() as i64, + // }; Ok(true) } OnlinePacket::Disconnect(_) => { // Disconnect the client immediately. - connection.disconnect("Client disconnected.", false); + // connection.disconnect("Client disconnected.", false); Ok(false) } OnlinePacket::NewConnection(_) => { - connection.state = ConnectionState::Connected; + // connection.state = ConnectionState::Connected; Ok(true) } _ => { @@ -305,40 +341,49 @@ impl Connection { Err(()) } - /// Handle a RakNet Event. These are sent as they happen. - /// - /// EG: - /// ```ignore - /// let conn: Connection = Connection::new(); - /// - /// while let Some((event, responder)) = conn.recv_ev { - /// match event { - /// ServerEvent::SetMtuSize(mtu) => { - /// println!("client updated mtu!"); - /// responder.send(ServerEventResponse::Acknowledge); - /// } - /// } - /// } - /// ``` - pub async fn recv_ev( - &self, - ) -> Result<(ServerEvent, oneshot::Sender), ConnectionError> { - match self.dispatch.lock().await.recv().await { - Some((server_event, event_responder)) => { - return Ok((server_event, event_responder)); - } - None => { - if self.disconnect.is_closed() { - return Err(ConnectionError::Closed); - } - return Err(ConnectionError::EventDispatchError); - } - } - } + // /// Handle a RakNet Event. These are sent as they happen. + // /// + // /// EG: + // /// ```ignore + // /// let conn: Connection = Connection::new(); + // /// + // /// while let Some((event, responder)) = conn.recv_ev { + // /// match event { + // /// ServerEvent::SetMtuSize(mtu) => { + // /// println!("client updated mtu!"); + // /// responder.send(ServerEventResponse::Acknowledge); + // /// } + // /// } + // /// } + // /// ``` + // pub async fn recv_ev( + // &mut self, + // ) -> Result<(ServerEvent, oneshot::Sender), ConnectionError> { + // match self.evt_receiver.recv().await { + // Some((server_event, event_responder)) => { + // return Ok((server_event, event_responder)); + // } + // None => { + // if self.disconnect.is_closed() { + // return Err(ConnectionError::Closed); + // } + // return Err(ConnectionError::EventDispatchError); + // } + // } + // } /// Initializes the client tick. pub async fn tick(&mut self) { let sendq = self.send_queue.write().await; // sendq.tick().await; } -} + + pub async fn close(&mut self) { + rakrs_debug!(true, "[{}] Dropping connection!", to_address_token(self.address)); + let tasks = self.tasks.clone(); + + for task in tasks.lock().await.drain(..) { + task.cancel().await; + } + } +} \ No newline at end of file diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index 7a557cc..e62a259 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -6,8 +6,10 @@ pub use self::send::*; use std::collections::HashMap; +use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; use crate::protocol::frame::FragmentMeta; use crate::protocol::frame::Frame; +use crate::protocol::reliability::Reliability; use crate::server::current_epoch; pub enum NetQueueError { @@ -59,12 +61,25 @@ pub struct RecoveryQueue { queue: HashMap, } -impl RecoveryQueue { +impl RecoveryQueue +where + Item: Clone +{ pub fn new() -> Self { Self { queue: HashMap::new(), } } + + pub fn insert_id(&mut self, seq: u32, item: Item) { + self.queue.insert(seq, (current_epoch(), item)); + } + + pub fn flush_old(&mut self, threshold: u64) -> Vec { + let old = self.queue.iter().filter(|(_, (time, _))| (*time + threshold) < current_epoch()).map(|(_, (_, item))| item.clone()).collect::>(); + self.queue.retain(|_, (time, _)| (*time + threshold) > current_epoch()); + old + } } impl NetQueue for RecoveryQueue { @@ -368,46 +383,50 @@ impl FragmentQueue { /// This will split a given frame into a bunch of smaller frames within the specified /// restriction. - pub fn split_insert(&mut self, frame: Frame, mtu: u16) -> Result { - let max_mtu = mtu - 60; + pub fn split_insert(&mut self, buffer: &[u8], mtu: u16) -> Result { + let max_mtu = mtu - RAKNET_HEADER_FRAME_OVERHEAD; + + self.fragment_id += self.fragment_id.wrapping_add(1); + + let id = self.fragment_id; + + if self.fragments.contains_key(&id) { + self.fragments.remove(&id); + } + + if let Ok(frames) = Self::split(buffer, id, mtu) { + self.fragments.insert(id, (frames.len() as u32, frames)); + return Ok(id); + } + + return Err(FragmentQueueError::DoesNotNeedSplit); + } + + pub fn split(buffer: &[u8], id: u16, mtu: u16) -> Result, FragmentQueueError> { + let max_mtu = mtu - RAKNET_HEADER_FRAME_OVERHEAD; - if frame.body.len() > max_mtu.into() { - let splits = frame - .body + if buffer.len() > max_mtu.into() { + let splits = buffer .chunks(max_mtu.into()) .map(|c| c.to_vec()) .collect::>>(); - let id = self.fragment_id.wrapping_add(1); - let mut index = 0; let mut frames: Vec = Vec::new(); + let mut index: u32 = 0; for buf in splits.iter() { - let mut f = Frame::init(); + let mut f = Frame::new(Reliability::ReliableOrd, Some(buf.clone())); f.fragment_meta = Some(FragmentMeta { index, size: splits.len() as u32, id, }); - f.reliability = frame.reliability; - f.flags = frame.flags; - f.size = buf.len() as u16; - f.body = buf.clone(); - f.order_index = frame.order_index; - f.order_channel = frame.order_channel; - f.reliable_index = frame.reliable_index; - index += 1; frames.push(f); } - if self.fragments.contains_key(&id) { - self.fragments.remove(&id); - } - - self.fragments.insert(id, (splits.len() as u32, frames)); - return Ok(id); + return Ok(frames); } return Err(FragmentQueueError::DoesNotNeedSplit); diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 6287c91..00b2c69 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -1,14 +1,26 @@ +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; + +use binary_utils::Streamable; +use async_std::net::UdpSocket; + +use crate::protocol::{RAKNET_HEADER_FRAME_OVERHEAD, RAKNET_HEADER_OVERHEAD}; use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; -use crate::protocol::frame::FramePacket; +use crate::protocol::frame::{FramePacket, Frame}; +use crate::protocol::packet::Packet; +use crate::protocol::reliability::Reliability; use crate::util::SafeGenerator; -use super::{NetQueue, RecoveryQueue}; +use super::{NetQueue, RecoveryQueue, FragmentQueue, OrderedQueue}; /// This queue is used to prioritize packets being sent out /// Packets that are old, are either dropped or requested again. /// You can define this behavior with the `timeout` property. #[derive(Debug, Clone)] pub struct SendQueue { + mtu_size: u16, + /// The amount of time that needs to pass for a packet to be /// dropped or requested again. timeout: u16, @@ -20,40 +32,94 @@ pub struct SendQueue { /// The current sequence number. This is incremented every time /// a packet is sent reliably. We can resend these if they are /// NAcked. - send_seq: u32, + send_seq: SafeGenerator, /// The current reliable index number. /// a packet is sent reliably an sequenced. - reliable_seq: SafeGenerator, + reliable_seq: SafeGenerator, /// The current recovery queue. - ack: RecoveryQueue>, + ack: RecoveryQueue, + + /// The fragment queue. + fragment_queue: FragmentQueue, + + order_channels: HashMap>>, + + ready: Vec, + + socket: Arc, + + address: SocketAddr, } impl SendQueue { - pub fn new() -> Self { + pub fn new(mtu_size: u16, timeout: u16, max_tries: u16, socket: Arc, address: SocketAddr) -> Self { Self { - timeout: 5000, - max_tries: 5, - send_seq: 0, + mtu_size, + timeout, + max_tries, + send_seq: SafeGenerator::new(), reliable_seq: SafeGenerator::new(), ack: RecoveryQueue::new(), + fragment_queue: FragmentQueue::new(), + order_channels: HashMap::new(), + ready: Vec::new(), + socket, + address, + } + } + + /// Send a packet based on its reliability. + /// Note, reliability will be set to `Reliability::ReliableOrd` if + /// the buffer is larger than max MTU. + pub async fn insert(&mut self, packet: Vec, reliability: Reliability, channel: u8, immediate: bool) { + let reliable = if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { + Reliability::ReliableOrd + } else { + reliability + }; + + // match reliability { + // Reliability::Unreliable => { + // // we can just send this packet out immediately. + // let frame = Frame::new(Reliability::Unreliable, Some(packet)); + // self.send_frame(frame).await; + // }, + // Reliability::Reliable => { + // // we need to send this packet out reliably. + // let frame = Frame::new(Reliability::Reliable, Some(packet)); + // self.send_frame(frame).await; + // }, + // } + } + + async fn send_frame(&mut self, mut frame: Frame) { + let mut pk = FramePacket::new(); + pk.sequence = self.send_seq.next(); + pk.reliability = frame.reliability; + + if pk.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + + if let Ok(buf) = pk.parse() { + self.send_socket(&buf[..]).await; } } - pub fn insert(&mut self, packet: Vec, reliable: bool) -> u32 { - // let next_id = self.fragment_seq.next(); - todo!() + async fn send_socket(&mut self, packet: &[u8]) { + self.socket.send_to(packet, &self.address).await.unwrap(); } - pub fn next_seq(&mut self) -> u32 { - let seq = self.send_seq; - self.send_seq = self.send_seq.wrapping_add(1); - return seq; + pub async fn send_packet(&mut self, packet: Packet, immediate: bool) { + // parse the packet } } impl Ackable for SendQueue { + type NackItem = FramePacket; + fn ack(&mut self, ack: Ack) { if ack.is_nack() { return; @@ -74,12 +140,12 @@ impl Ackable for SendQueue { } } - fn nack(&mut self, nack: Ack) -> Vec> { + fn nack(&mut self, nack: Ack) -> Vec { if !nack.is_nack() { return Vec::new(); } - let mut resend_queue = Vec::>::new(); + let mut resend_queue = Vec::::new(); // we need to get the packets to resend. for record in nack.records.iter() { diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 2a1cd51..0bea23b 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -7,6 +7,8 @@ use binary_utils::Streamable; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; pub(crate) trait Ackable { + type NackItem; + /// When an ack packet is recieved. /// We should ack the queue fn ack(&mut self, index: Ack) {} @@ -14,7 +16,7 @@ pub(crate) trait Ackable { /// When an NACK packet is recieved. /// We should nack the queue /// This should return the packets that need to be resent. - fn nack(&mut self, packet: Ack) -> Vec> { + fn nack(&mut self, packet: Ack) -> Vec { todo!() } } diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 01cf335..41283fc 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -209,6 +209,25 @@ impl Frame { } } + /// Initializes a new frame with the given reliability. + pub fn new(reliability: Reliability, body: Option>) -> Self { + Self { + flags: 0, + size: if let Some(b) = body.as_ref() { + b.len() as u16 + } else { + 0 + }, + reliable_index: None, + sequence_index: None, + order_index: None, + order_channel: None, + fragment_meta: None, + reliability, + body: body.unwrap_or(Vec::new()), + } + } + /// Whether or not the frame is fragmented. pub fn is_fragmented(&self) -> bool { self.fragment_meta.is_some() diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 10f4c87..a656068 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -9,3 +9,8 @@ pub use magic::*; pub const MAX_FRAGS: u32 = 1024; pub const MAX_ORD_CHANS: u8 = 32; + +// IP Header + UDP Header + RakNet Header + RakNet Frame Packet Header (MAX) +pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20; +// IP Header + UDP Header + RakNet Header +pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 96b5495..c045af3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -3,12 +3,19 @@ pub mod event; use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use std::time::Duration; use std::{net::SocketAddr, sync::Arc}; +use async_std::io::timeout; use binary_utils::Streamable; -use tokio::net::UdpSocket; -use tokio::sync::{mpsc, oneshot}; -use tokio::sync::{Mutex, Notify, Semaphore}; +#[cfg(feature = "async-std")] +use async_std::net::UdpSocket; +use async_std::channel::{Sender, Receiver, bounded}; +use async_std::channel::TrySendError; +use async_std::channel::unbounded; +use async_std::sync::{Mutex, Condvar}; +use futures::select; use crate::connection::{ConnMeta, Connection}; use crate::error::server::ServerError; @@ -27,8 +34,7 @@ use self::event::ServerEvent; pub type Session = ( ConnMeta, - mpsc::Sender>, - mpsc::Sender<(ServerEvent, oneshot::Sender)>, + Sender> ); // stupid hack for easier syntax :) @@ -92,14 +98,18 @@ pub struct Listener { connections: Arc>>, /// The recieve communication channel, This is used to dispatch connections between a handle /// It allows you to use the syntax sugar for `Listener::accept()`. - recv_comm: mpsc::Receiver, - send_comm: mpsc::Sender, + recv_comm: Receiver, + send_comm: Sender, + // todo, fix this! + // send_evnt: Sender<(ServerEvent, oneshot::Sender)>, + // pub recv_evnt: Arc)>>>, + // todo /// A Notifier (sephamore) that will wait until all notified listeners /// are completed, and finish closing. - closer: Arc, + closed: Arc, /// This is a notifier that acknowledges all connections have been removed from the server successfully. /// This is important to prevent memory leaks if the process is continously running. - cleanup: Arc, + cleanup: Arc, } impl Listener { @@ -125,11 +135,12 @@ impl Listener { let motd = Motd::new(server_id, format!("{}", address.port())); // This channel is a Communication channel for when `Connection` structs are initialized. - let (send_comm, recv_comm) = mpsc::channel::(10); + let (send_comm, recv_comm) = bounded::(10); // This channel is responsible for handling and dispatching events between clients. // Oneshot will garauntee this event is intended for the client whom requested the event. - let (send_evnt, recv_evnt) = - mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); + // todo: Fix with new event system + // let (send_evnt, recv_evnt) = + // mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); let listener = Self { sock: Some(Arc::new(sock)), @@ -138,10 +149,14 @@ impl Listener { motd, send_comm, recv_comm, + // send_evnt, + // recv_evnt: Arc::new(Mutex::new(recv_evnt)), serving: false, connections: Arc::new(Mutex::new(HashMap::new())), - closer: Arc::new(Semaphore::new(0)), - cleanup: Arc::new(Notify::new()), + // closer: Arc::new(Semaphore::new(0)), + closed: Arc::new(AtomicBool::new(false)), + // cleanup: Arc::new(Notify::new()), + cleanup: Arc::new(Condvar::new()), }; return Ok(listener); @@ -164,96 +179,41 @@ impl Listener { let socket = self.sock.as_ref().unwrap().clone(); let send_comm = self.send_comm.clone(); + // let send_evt = self.send_evnt.clone(); let server_id = self.id.clone(); let default_motd = self.motd.clone(); let connections = self.connections.clone(); - let closer = self.closer.clone(); + let closer = self.closed.clone(); let cleanup = self.cleanup.clone(); let versions = self.versions.clone(); self.serving = true; - tokio::spawn(async move { + async_std::task::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; let motd_default = default_motd.clone(); loop { - // The socket is not readable. We can not read it. - if socket.readable().await.is_err() { - continue; - } - let length: usize; let origin: SocketAddr; // We need to wait for either the socket to stop recieving, // or a server close notification. - tokio::select! { - recv = socket.recv_from(&mut buf) => { - match recv { - Ok((l, o)) => { - length = l; - origin = o; - }, - Err(_) => continue - } - }, - _ = closer.acquire() => { - // we got a close notification, disconnect all clients - let mut sessions = connections.lock().await; - for conn in sessions.drain() { - // send a disconnect notification to each connection - let disconnect = Disconnect {}; - send_packet_to_socket(&socket, disconnect.into(), conn.0).await; - - // absolutely ensure disconnection - let (rx, _) = oneshot::channel::(); - if let Err(_) = conn.1.2.send((ServerEvent::DisconnectImmediately, rx)).await { - // failed to send the connection, we're gonna drop it either way - } - drop(conn); + let recv = socket.recv_from(&mut buf).await; + match recv { + Ok((l, o)) => { + length = l; + origin = o; + }, + Err(e) => { + rakrs_debug!(true, "Error: {:?}", e); + continue; } - - cleanup.notify_one(); - - break; } - }; // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { - // This is a valid packet, let's check if a session exists, if not, we should create it. - // Event if the connection is only in offline mode. - let mut sessions = connections.lock().await; - - if !sessions.contains_key(&origin) { - let meta = ConnMeta::new(0); - let (net_send, net_recv) = mpsc::channel::>(10); - let (evt_send, evt_recv) = mpsc::channel::<( - ServerEvent, - oneshot::Sender, - )>(10); - let connection = Connection::new(origin, &socket, net_recv, evt_recv).await; - - // Add the connection to the available connections list. - // we're using the name "sessions" here to differeniate - sessions.insert(origin, (meta, net_send, evt_send)); - - // notify the connection communicator - if let Err(err) = send_comm.send(connection).await { - let connection = err.0; - // there was an error, and we should terminate this connection immediately. - rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); - sessions.remove(&origin); - continue; - } - } - - // We're dropping here because we don't know if we'll need it - // We don't want to hold the lock longer than we need to. - drop(sessions); - // if this is an offline packet, we can retrieve the buffer we should send. match packet.payload { Payload::Offline(pk) => { @@ -262,35 +222,38 @@ impl Listener { // raknet protocol, and handshaking. match pk { OfflinePacket::UnconnectedPing(_) => { - let (resp_tx, resp_rx) = - oneshot::channel::(); + // let (resp_tx, resp_rx) = + // oneshot::channel::(); let mut motd: Motd = motd_default.clone(); - let sessions = connections.lock().await; - - if let Err(_) = sessions[&origin] - .2 - .send(( - ServerEvent::RefreshMotdRequest(origin, motd.clone()), - resp_tx, - )) - .await - { - // todo event error, - // we're gonna ignore it and continue by sending default motd. - rakrs_debug!("[{}] Encountered an error when dispatching ServerEvent::RefreshMotdRequest.", to_address_token(*&origin)) - } - if let Ok(res) = resp_rx.await { - // get the motd from the server event otherwise use defaults. - match res { - ServerEventResponse::RefreshMotd(m) => { - motd = m; - } - _ => { - rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); - } - }; - } + // if let Err(e) = send_evt.try_send(( + // ServerEvent::RefreshMotdRequest(origin, motd.clone()), + // // resp_tx, + // )) + // { + // match e { + // TrySendError::Full(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); + // } + // TrySendError::Closed(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); + // } + // } + // } + + // if let Ok(res) = resp_rx.await { + // // get the motd from the server event otherwise use defaults. + // // if let Ok(res) = res { + // match res { + // ServerEventResponse::RefreshMotd(m) => { + // motd = m; + // } + // _ => { + // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); + // } + // } + // // }; + // } // unconnected pong signature is different if MCPE is specified. let resp = UnconnectedPong { @@ -351,34 +314,70 @@ impl Listener { security: false, }; + // This is a valid packet, let's check if a session exists, if not, we should create it. + // Event if the connection is only in offline mode. + let mut sessions = connections.lock().await; + + if !sessions.contains_key(&origin) { + rakrs_debug!(true, "Creating new session for {}", origin); + let meta = ConnMeta::new(0); + let (net_send, net_recv) = bounded::>(10); + let connection = Connection::new(origin, &socket, net_recv, pk.mtu_size).await; + rakrs_debug!(true, "Created Session for {}", origin); + + // Add the connection to the available connections list. + // we're using the name "sessions" here to differeniate + // for some reason the reciever likes to be dropped, so we're saving it here. + sessions.insert(origin, (meta, net_send)); + + // notify the connection communicator + if let Err(err) = send_comm.send(connection).await { + let connection = err.0; + // there was an error, and we should terminate this connection immediately. + rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); + sessions.remove(&origin); + continue; + } + } + // update the sessions mtuSize, this is referred to internally, we also will send this event to the client // event channel. However we are not expecting a response. drop( sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size, ); - let (resp_tx, _) = oneshot::channel::(); - if let Err(_) = sessions[&origin] - .2 - .send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) - .await - { - rakrs_debug!( - "[{}] Failed to update mtu size with the client!", - to_address_token(origin) - ); - } + // let (resp_tx, resp_rx) = oneshot::channel::(); + + // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) + // .await + // { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } send_packet_to_socket(&socket, resp.into(), origin).await; continue; } _ => { - // everything else should be sent to the socket + rakrs_debug!( + "[{}] Received invalid packet!", + to_address_token(*&origin) + ); } } } _ => {} }; + } // Packet may be valid, but we'll let the connection decide this @@ -388,12 +387,32 @@ impl Listener { rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed!", to_address_token(*&origin)); } } + drop(sessions); } }); return Ok(()); } + // pub async fn recv_event(&self) -> Result<(ServerEvent, oneshot::Sender), ServerError> { + // if !self.serving { + // Err(ServerError::NotListening) + // } else { + // let mut recvr = self.recv_evnt.lock().await; + // tokio::select! { + // receiver = recvr.recv() => { + // match receiver { + // Some(c) => Ok(c), + // None => Err(ServerError::Killed) + // } + // }, + // _ = self.closer.acquire() => { + // Err(ServerError::Killed) + // } + // } + // } + // } + /// Must be called in after both `Listener::bind` AND `Listener::start`. This function /// is used to recieve and accept connections. Alternatively, you can refuse a connection /// by dropping it when you accept it. @@ -401,15 +420,13 @@ impl Listener { if !self.serving { Err(ServerError::NotListening) } else { - tokio::select! { - receiver = self.recv_comm.recv() => { - match receiver { - Some(c) => Ok(c), - None => Err(ServerError::Killed) - } - }, - _ = self.closer.acquire() => { - Err(ServerError::Killed) + if self.closed.load(std::sync::atomic::Ordering::Relaxed) { + return Err(ServerError::Killed); + } else { + let receiver = self.recv_comm.recv().await; + return match receiver { + Ok(c) => Ok(c), + Err(_) => Err(ServerError::Killed) } } } @@ -417,12 +434,12 @@ impl Listener { /// Stops the listener and frees the socket. pub async fn stop(&mut self) -> Result<(), ServerError> { - if self.closer.is_closed() { + if self.closed.load(std::sync::atomic::Ordering::Relaxed) { return Ok(()); } - self.closer.close(); - self.cleanup.notified().await; + self.closed.store(true, std::sync::atomic::Ordering::Relaxed); + // self.cleanup.notified().await; self.sock = None; self.serving = false; diff --git a/src/util/mod.rs b/src/util/mod.rs index 5a52705..88423d6 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,5 +1,9 @@ use std::net::{SocketAddr, ToSocketAddrs}; use std::{collections::HashMap, time::SystemTime}; +#[cfg(feature="async-std")] +use async_std::task::sleep as async_sleep; +#[cfg(feature="tokio")] +use tokio::time::sleep as async_sleep; pub(crate) mod debug; @@ -121,3 +125,8 @@ pub fn from_address_token(remote: String) -> SocketAddr { .expect("Could not parse remote address."); SocketAddr::from(parsed.next().unwrap()) } + + +pub async fn sleep(duration: std::time::Duration) { + async_sleep(duration).await; +} \ No newline at end of file From 406179e39aba753932604f4a9a35a6e55610775c Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 20:05:43 -0600 Subject: [PATCH 21/75] chore: Add moreee stufffff --- src/connection/mod.rs | 6 +++++- src/server/mod.rs | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/connection/mod.rs b/src/connection/mod.rs index dd1aa08..9f45058 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -189,6 +189,8 @@ impl Connection { break; } + rakrs_debug!(true, "[{}] Waiting for packet...", to_address_token(address)); + if let Ok(payload) = net.recv().await { // We've recieved a payload! recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); @@ -214,6 +216,7 @@ impl Connection { if v == true { // DISCONNECT // disconnect.close(); + rakrs_debug!(true, "[{}] Connection::process_packet returned true!", to_address_token(address)); disconnect.store(true, std::sync::atomic::Ordering::Relaxed); break; } @@ -287,7 +290,7 @@ impl Connection { ) -> Result { if let Ok(packet) = Packet::compose(buffer, &mut 0) { if packet.is_online() { - println!("Recieved packet: {:?}", packet); + rakrs_debug!(true, "[{}] Recieved packet: {:?}", to_address_token(*address), packet); return match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { @@ -312,6 +315,7 @@ impl Connection { // .unwrap() // .as_millis() as i64, // }; + rakrs_debug!(true, "[{}] ConnectionRequest not implemented, disconnecting!", to_address_token(*address)); Ok(true) } OnlinePacket::Disconnect(_) => { diff --git a/src/server/mod.rs b/src/server/mod.rs index c045af3..e364a4d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -305,7 +305,6 @@ impl Listener { continue; } OfflinePacket::SessionInfoRequest(pk) => { - let mut sessions = connections.lock().await; let resp = SessionInfoReply { server_id, client_address: origin, @@ -314,7 +313,7 @@ impl Listener { security: false, }; - // This is a valid packet, let's check if a session exists, if not, we should create it. + // This is a valid packet, let's check if a session exists, if not, we should create it. // Event if the connection is only in offline mode. let mut sessions = connections.lock().await; @@ -381,10 +380,11 @@ impl Listener { } // Packet may be valid, but we'll let the connection decide this - let sessions = connections.lock().await; + let mut sessions = connections.lock().await; if sessions.contains_key(&origin) { if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { - rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed!", to_address_token(*&origin)); + rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed! (Removed the connection)", to_address_token(*&origin)); + sessions.remove(&origin); } } drop(sessions); From 5c37da9b3d62227a7998a9aa80e711456f7776a5 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 20:32:23 -0600 Subject: [PATCH 22/75] feat: nasty hacks for both tokio and async std :) --- src/connection/mod.rs | 240 ++++++++++++++++++++--------------- src/connection/queue/mod.rs | 14 +- src/connection/queue/send.rs | 27 +++- src/protocol/mod.rs | 2 +- src/server/mod.rs | 86 ++++++++----- src/util/mod.rs | 9 +- 6 files changed, 232 insertions(+), 146 deletions(-) diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 9f45058..c005e89 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -3,50 +3,56 @@ pub mod controller; pub mod queue; pub mod state; -use std::{net::SocketAddr, sync::{Arc, atomic::{AtomicU64, AtomicBool}}, time::{SystemTime, Duration}}; +use std::{ + net::SocketAddr, + sync::{ + atomic::{AtomicBool, AtomicU64}, + Arc, + }, + time::{Duration, SystemTime}, +}; -use async_std::{channel::{self, bounded}, task::JoinHandle}; -use binary_utils::Streamable; #[cfg(feature = "async-std")] use async_std::{ + channel::{bounded, Receiver, Sender}, net::UdpSocket, - channel::{ - Sender, Receiver - }, - sync::{ - Condvar, Mutex, RwLock, - }, task::sleep, + sync::{Mutex, RwLock}, + task::{self, sleep, JoinHandle}, }; +use binary_utils::Streamable; #[cfg(feature = "tokio")] use tokio::{ net::UdpSocket, sync::{ - mpsc::{self, Sender}, - oneshot, Mutex, Notify, RwLock, Semaphore, - }, time::{timeout, sleep}, + mpsc::{channel as bounded, Receiver, Sender}, + Mutex, RwLock, + }, + task::{ + self, + JoinHandle + }, + time::sleep, }; use crate::{ - error::connection::ConnectionError, protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, - packet::{online::{OnlinePacket, ConnectedPong}, Packet}, + packet::{ + online::{ConnectedPong, OnlinePacket}, + Packet, + }, }, rakrs_debug, - server::{ - current_epoch, - event::{ServerEvent, ServerEventResponse}, - }, + server::current_epoch, util::to_address_token, }; use self::{ - controller::window::ReliableWindow, queue::{RecvQueue, SendQueue}, state::ConnectionState, }; -pub(crate) type ConnNetChan = Arc>>>; +pub(crate) type ConnNetChan = Arc>>>; #[derive(Debug, Clone, Copy)] pub struct ConnMeta { @@ -105,14 +111,20 @@ impl Connection { pub async fn new( address: SocketAddr, socket: &Arc, - net: channel::Receiver>, + net: Receiver>, mtu: u16, ) -> Self { let (net_sender, net_receiver) = bounded::>(100); // let (evt_sender, evt_receiver) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); let c = Self { address, - send_queue: Arc::new(RwLock::new(SendQueue::new(mtu, 12000, 5, socket.clone(), address))), + send_queue: Arc::new(RwLock::new(SendQueue::new( + mtu, + 12000, + 5, + socket.clone(), + address, + ))), recv_queue: Arc::new(Mutex::new(RecvQueue::new())), internal_net_recv: Arc::new(Mutex::new(net_receiver)), // evt_sender, @@ -137,7 +149,7 @@ impl Connection { } /// Initializes the client ticking process! - pub fn init_tick(&self, socket: &Arc) -> async_std::task::JoinHandle<()>{ + pub fn init_tick(&self, socket: &Arc) -> task::JoinHandle<()> { let address = self.address; let closer = self.disconnect.clone(); let last_recv = self.recv_time.clone(); @@ -145,7 +157,7 @@ impl Connection { // initialize the event io // we initialize the ticking function here, it's purpose is to update the state of the current connection // while handling throttle - return async_std::task::spawn(async move { + return task::spawn(async move { loop { sleep(Duration::from_millis(50)).await; @@ -170,7 +182,7 @@ impl Connection { socket: &Arc, mut net: Receiver>, sender: Sender>, - ) -> async_std::task::JoinHandle<()> { + ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); let recv_q = self.recv_queue.clone(); let send_q = self.send_queue.clone(); @@ -178,7 +190,7 @@ impl Connection { let state = self.state.clone(); let address = self.address; - return async_std::task::spawn(async move { + return task::spawn(async move { loop { if disconnect.load(std::sync::atomic::Ordering::Relaxed) { rakrs_debug!( @@ -189,94 +201,108 @@ impl Connection { break; } - rakrs_debug!(true, "[{}] Waiting for packet...", to_address_token(address)); + rakrs_debug!( + true, + "[{}] Waiting for packet...", + to_address_token(address) + ); - if let Ok(payload) = net.recv().await { - // We've recieved a payload! - recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + macro_rules! handle_payload { + ($payload: ident) => { + // We've recieved a payload! + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); - let id = payload[0]; - match id { - // This is a frame packet. - // This packet will be handled by the recv_queue - 0x80..=0x8d => { - if let Ok(pk) = FramePacket::compose(&payload[..], &mut 0) { - let mut rq = recv_q.lock().await; + let id = $payload[0]; + match id { + // This is a frame packet. + // This packet will be handled by the recv_queue + 0x80..=0x8d => { + if let Ok(pk) = FramePacket::compose(&$payload[..], &mut 0) { + let mut rq = recv_q.lock().await; - if let Ok(_) = rq.insert(pk) {}; + if let Ok(_) = rq.insert(pk) {}; - let buffers = rq.flush(); + let buffers = rq.flush(); - for buffer in buffers { - let res = Connection::process_packet( - &buffer, &address, &sender, &send_q, &state, - ) - .await; - if let Ok(v) = res { - if v == true { - // DISCONNECT - // disconnect.close(); - rakrs_debug!(true, "[{}] Connection::process_packet returned true!", to_address_token(address)); - disconnect.store(true, std::sync::atomic::Ordering::Relaxed); - break; + for buffer in buffers { + let res = Connection::process_packet( + &buffer, &address, &sender, &send_q, &state, + ) + .await; + if let Ok(v) = res { + if v == true { + // DISCONNECT + // disconnect.close(); + rakrs_debug!(true, "[{}] Connection::process_packet returned true!", to_address_token(address)); + disconnect.store(true, std::sync::atomic::Ordering::Relaxed); + break; + } } + if let Err(_) = res { + rakrs_debug!( + true, + "[{}] Failed to process packet!", + to_address_token(address) + ); + }; } - if let Err(_) = res { - rakrs_debug!( - true, - "[{}] Failed to process packet!", - to_address_token(address) - ); - }; - } - drop(rq); + drop(rq); + } } - } - NACK => { - // Validate this is a nack packet - if let Ok(nack) = Ack::compose(&payload[..], &mut 0) { - // The client acknowledges it did not recieve these packets - // We should resend them. - let mut sq = send_q.write().await; - let resend = sq.nack(nack); - for packet in resend { - if let Ok(buffer) = packet.parse() { - if let Err(_) = sender.send(buffer).await { + NACK => { + // Validate this is a nack packet + if let Ok(nack) = Ack::compose(&$payload[..], &mut 0) { + // The client acknowledges it did not recieve these packets + // We should resend them. + let mut sq = send_q.write().await; + let resend = sq.nack(nack); + for packet in resend { + if let Ok(buffer) = packet.parse() { + if let Err(_) = sender.send(buffer).await { + rakrs_debug!( + true, + "[{}] Failed to send packet to client!", + to_address_token(address) + ); + } + } else { rakrs_debug!( true, - "[{}] Failed to send packet to client!", + "[{}] Failed to send packet to client (parsing failed)!", to_address_token(address) ); } - } else { - rakrs_debug!( - true, - "[{}] Failed to send packet to client (parsing failed)!", - to_address_token(address) - ); } } } - } - ACK => { - // first lets validate this is an ack packet - if let Ok(ack) = Ack::compose(&payload[..], &mut 0) { - // The client acknowledges it recieved these packets - // We should remove them from the queue. - let mut sq = send_q.write().await; - sq.ack(ack); + ACK => { + // first lets validate this is an ack packet + if let Ok(ack) = Ack::compose(&$payload[..], &mut 0) { + // The client acknowledges it recieved these packets + // We should remove them from the queue. + let mut sq = send_q.write().await; + sq.ack(ack); + } + } + _ => { + rakrs_debug!( + true, + "[{}] Unknown RakNet packet recieved (Or packet is sent out of scope).", + to_address_token(address) + ); } - } - _ => { - rakrs_debug!( - true, - "[{}] Unknown RakNet packet recieved (Or packet is sent out of scope).", - to_address_token(address) - ); - } + }; }; } + + match net.recv().await { + #[cfg(feature = "async-std")] + Ok(payload) => {handle_payload!(payload);}, + #[cfg(feature = "tokio")] + Some(payload) => {handle_payload!(payload);}, + _ => continue, + } } }); } @@ -290,12 +316,17 @@ impl Connection { ) -> Result { if let Ok(packet) = Packet::compose(buffer, &mut 0) { if packet.is_online() { - rakrs_debug!(true, "[{}] Recieved packet: {:?}", to_address_token(*address), packet); + rakrs_debug!( + true, + "[{}] Recieved packet: {:?}", + to_address_token(*address), + packet + ); return match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { ping_time: pk.time, - pong_time: current_epoch() as i64 + pong_time: current_epoch() as i64, }; let q = send_q.write().await; // q.insert_immediate(response); @@ -315,7 +346,11 @@ impl Connection { // .unwrap() // .as_millis() as i64, // }; - rakrs_debug!(true, "[{}] ConnectionRequest not implemented, disconnecting!", to_address_token(*address)); + rakrs_debug!( + true, + "[{}] ConnectionRequest not implemented, disconnecting!", + to_address_token(*address) + ); Ok(true) } OnlinePacket::Disconnect(_) => { @@ -383,11 +418,18 @@ impl Connection { } pub async fn close(&mut self) { - rakrs_debug!(true, "[{}] Dropping connection!", to_address_token(self.address)); + rakrs_debug!( + true, + "[{}] Dropping connection!", + to_address_token(self.address) + ); let tasks = self.tasks.clone(); for task in tasks.lock().await.drain(..) { + #[cfg(feature = "async-std")] task.cancel().await; + #[cfg(feature = "tokio")] + task.abort(); } } -} \ No newline at end of file +} diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index e62a259..fc2c985 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -6,10 +6,10 @@ pub use self::send::*; use std::collections::HashMap; -use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; use crate::protocol::frame::FragmentMeta; use crate::protocol::frame::Frame; use crate::protocol::reliability::Reliability; +use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; use crate::server::current_epoch; pub enum NetQueueError { @@ -63,7 +63,7 @@ pub struct RecoveryQueue { impl RecoveryQueue where - Item: Clone + Item: Clone, { pub fn new() -> Self { Self { @@ -76,8 +76,14 @@ where } pub fn flush_old(&mut self, threshold: u64) -> Vec { - let old = self.queue.iter().filter(|(_, (time, _))| (*time + threshold) < current_epoch()).map(|(_, (_, item))| item.clone()).collect::>(); - self.queue.retain(|_, (time, _)| (*time + threshold) > current_epoch()); + let old = self + .queue + .iter() + .filter(|(_, (time, _))| (*time + threshold) < current_epoch()) + .map(|(_, (_, item))| item.clone()) + .collect::>(); + self.queue + .retain(|_, (time, _)| (*time + threshold) > current_epoch()); old } } diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 00b2c69..85d5684 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -2,17 +2,20 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; -use binary_utils::Streamable; +#[cfg(feature = "async-std")] use async_std::net::UdpSocket; +use binary_utils::Streamable; +#[cfg(feature = "tokio")] +use tokio::net::UdpSocket; -use crate::protocol::{RAKNET_HEADER_FRAME_OVERHEAD, RAKNET_HEADER_OVERHEAD}; use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; -use crate::protocol::frame::{FramePacket, Frame}; +use crate::protocol::frame::{Frame, FramePacket}; use crate::protocol::packet::Packet; use crate::protocol::reliability::Reliability; +use crate::protocol::{RAKNET_HEADER_FRAME_OVERHEAD, RAKNET_HEADER_OVERHEAD}; use crate::util::SafeGenerator; -use super::{NetQueue, RecoveryQueue, FragmentQueue, OrderedQueue}; +use super::{FragmentQueue, NetQueue, OrderedQueue, RecoveryQueue}; /// This queue is used to prioritize packets being sent out /// Packets that are old, are either dropped or requested again. @@ -54,7 +57,13 @@ pub struct SendQueue { } impl SendQueue { - pub fn new(mtu_size: u16, timeout: u16, max_tries: u16, socket: Arc, address: SocketAddr) -> Self { + pub fn new( + mtu_size: u16, + timeout: u16, + max_tries: u16, + socket: Arc, + address: SocketAddr, + ) -> Self { Self { mtu_size, timeout, @@ -73,7 +82,13 @@ impl SendQueue { /// Send a packet based on its reliability. /// Note, reliability will be set to `Reliability::ReliableOrd` if /// the buffer is larger than max MTU. - pub async fn insert(&mut self, packet: Vec, reliability: Reliability, channel: u8, immediate: bool) { + pub async fn insert( + &mut self, + packet: Vec, + reliability: Reliability, + channel: u8, + immediate: bool, + ) { let reliable = if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { Reliability::ReliableOrd } else { diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index a656068..af955c9 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -13,4 +13,4 @@ pub const MAX_ORD_CHANS: u8 = 32; // IP Header + UDP Header + RakNet Header + RakNet Frame Packet Header (MAX) pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20; // IP Header + UDP Header + RakNet Header -pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; \ No newline at end of file +pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; diff --git a/src/server/mod.rs b/src/server/mod.rs index e364a4d..86c20e2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,16 +7,35 @@ use std::sync::atomic::AtomicBool; use std::time::Duration; use std::{net::SocketAddr, sync::Arc}; -use async_std::io::timeout; +#[cfg(feature = "async-std")] +use async_std::{ + channel::unbounded, + channel::TrySendError, + channel::{bounded, Receiver, Sender}, + task::{ + self, + }, + io::timeout, + net::UdpSocket, + sync::{Condvar, Mutex}, +}; use binary_utils::Streamable; #[cfg(feature = "async-std")] -use async_std::net::UdpSocket; -use async_std::channel::{Sender, Receiver, bounded}; -use async_std::channel::TrySendError; -use async_std::channel::unbounded; -use async_std::sync::{Mutex, Condvar}; use futures::select; +#[cfg(feature = "tokio")] +use tokio::{ + sync::mpsc::channel as bounded, + sync::mpsc::{Receiver, Sender}, + task::{ + self, + }, + net::UdpSocket, + time::timeout, + sync::{Mutex}, +}; + + use crate::connection::{ConnMeta, Connection}; use crate::error::server::ServerError; use crate::protocol::mcpe::motd::Motd; @@ -32,10 +51,7 @@ use crate::util::to_address_token; use self::event::ServerEvent; -pub type Session = ( - ConnMeta, - Sender> -); +pub type Session = (ConnMeta, Sender>); // stupid hack for easier syntax :) pub enum PossiblySocketAddr<'a> { @@ -107,9 +123,9 @@ pub struct Listener { /// A Notifier (sephamore) that will wait until all notified listeners /// are completed, and finish closing. closed: Arc, - /// This is a notifier that acknowledges all connections have been removed from the server successfully. - /// This is important to prevent memory leaks if the process is continously running. - cleanup: Arc, + // This is a notifier that acknowledges all connections have been removed from the server successfully. + // This is important to prevent memory leaks if the process is continously running. + // cleanup: Arc, } impl Listener { @@ -156,7 +172,7 @@ impl Listener { // closer: Arc::new(Semaphore::new(0)), closed: Arc::new(AtomicBool::new(false)), // cleanup: Arc::new(Notify::new()), - cleanup: Arc::new(Condvar::new()), + // cleanup: Arc::new(Condvar::new()), }; return Ok(listener); @@ -184,12 +200,12 @@ impl Listener { let default_motd = self.motd.clone(); let connections = self.connections.clone(); let closer = self.closed.clone(); - let cleanup = self.cleanup.clone(); + // let cleanup = self.cleanup.clone(); let versions = self.versions.clone(); self.serving = true; - async_std::task::spawn(async move { + task::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; let motd_default = default_motd.clone(); @@ -200,17 +216,17 @@ impl Listener { // We need to wait for either the socket to stop recieving, // or a server close notification. - let recv = socket.recv_from(&mut buf).await; - match recv { - Ok((l, o)) => { - length = l; - origin = o; - }, - Err(e) => { - rakrs_debug!(true, "Error: {:?}", e); - continue; - } + let recv = socket.recv_from(&mut buf).await; + match recv { + Ok((l, o)) => { + length = l; + origin = o; + } + Err(e) => { + rakrs_debug!(true, "Error: {:?}", e); + continue; } + } // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { @@ -321,7 +337,9 @@ impl Listener { rakrs_debug!(true, "Creating new session for {}", origin); let meta = ConnMeta::new(0); let (net_send, net_recv) = bounded::>(10); - let connection = Connection::new(origin, &socket, net_recv, pk.mtu_size).await; + let connection = + Connection::new(origin, &socket, net_recv, pk.mtu_size) + .await; rakrs_debug!(true, "Created Session for {}", origin); // Add the connection to the available connections list. @@ -376,7 +394,6 @@ impl Listener { } _ => {} }; - } // Packet may be valid, but we'll let the connection decide this @@ -425,9 +442,15 @@ impl Listener { } else { let receiver = self.recv_comm.recv().await; return match receiver { + #[cfg(feature = "async-std")] Ok(c) => Ok(c), - Err(_) => Err(ServerError::Killed) - } + #[cfg(feature = "async-std")] + Err(_) => Err(ServerError::Killed), + #[cfg(feature = "tokio")] + Some(c) => Ok(c), + #[cfg(feature = "tokio")] + None => Err(ServerError::Killed), + }; } } } @@ -438,7 +461,8 @@ impl Listener { return Ok(()); } - self.closed.store(true, std::sync::atomic::Ordering::Relaxed); + self.closed + .store(true, std::sync::atomic::Ordering::Relaxed); // self.cleanup.notified().await; self.sock = None; diff --git a/src/util/mod.rs b/src/util/mod.rs index 88423d6..92268e1 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,8 @@ +#[cfg(feature = "async-std")] +use async_std::task::sleep as async_sleep; use std::net::{SocketAddr, ToSocketAddrs}; use std::{collections::HashMap, time::SystemTime}; -#[cfg(feature="async-std")] -use async_std::task::sleep as async_sleep; -#[cfg(feature="tokio")] +#[cfg(feature = "tokio")] use tokio::time::sleep as async_sleep; pub(crate) mod debug; @@ -126,7 +126,6 @@ pub fn from_address_token(remote: String) -> SocketAddr { SocketAddr::from(parsed.next().unwrap()) } - pub async fn sleep(duration: std::time::Duration) { async_sleep(duration).await; -} \ No newline at end of file +} From 7f716da65c189281fdab5fc5120c52a789a0fa9a Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 20:34:14 -0600 Subject: [PATCH 23/75] chore: fmt --- src/connection/mod.rs | 13 +++++++------ src/server/mod.rs | 13 ++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/connection/mod.rs b/src/connection/mod.rs index c005e89..c761320 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -27,10 +27,7 @@ use tokio::{ mpsc::{channel as bounded, Receiver, Sender}, Mutex, RwLock, }, - task::{ - self, - JoinHandle - }, + task::{self, JoinHandle}, time::sleep, }; @@ -298,9 +295,13 @@ impl Connection { match net.recv().await { #[cfg(feature = "async-std")] - Ok(payload) => {handle_payload!(payload);}, + Ok(payload) => { + handle_payload!(payload); + } #[cfg(feature = "tokio")] - Some(payload) => {handle_payload!(payload);}, + Some(payload) => { + handle_payload!(payload); + } _ => continue, } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 86c20e2..984709c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -12,12 +12,10 @@ use async_std::{ channel::unbounded, channel::TrySendError, channel::{bounded, Receiver, Sender}, - task::{ - self, - }, io::timeout, net::UdpSocket, sync::{Condvar, Mutex}, + task::{self}, }; use binary_utils::Streamable; #[cfg(feature = "async-std")] @@ -25,17 +23,14 @@ use futures::select; #[cfg(feature = "tokio")] use tokio::{ + net::UdpSocket, sync::mpsc::channel as bounded, sync::mpsc::{Receiver, Sender}, - task::{ - self, - }, - net::UdpSocket, + sync::Mutex, + task::{self}, time::timeout, - sync::{Mutex}, }; - use crate::connection::{ConnMeta, Connection}; use crate::error::server::ServerError; use crate::protocol::mcpe::motd::Motd; From 40f8bbab1e62464fe142c1140667e3752c4c4dbf Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 20:51:41 -0600 Subject: [PATCH 24/75] chore: Stablize both async-std and tokio features (tokio still remains somewhat unstable) --- src/server/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 984709c..e11c660 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,7 +4,6 @@ pub mod event; use std::collections::HashMap; use std::sync::atomic::AtomicBool; -use std::time::Duration; use std::{net::SocketAddr, sync::Arc}; #[cfg(feature = "async-std")] @@ -28,7 +27,6 @@ use tokio::{ sync::mpsc::{Receiver, Sender}, sync::Mutex, task::{self}, - time::timeout, }; use crate::connection::{ConnMeta, Connection}; @@ -441,9 +439,9 @@ impl Listener { Ok(c) => Ok(c), #[cfg(feature = "async-std")] Err(_) => Err(ServerError::Killed), - #[cfg(feature = "tokio")] + #[cfg(not(feature = "async-std"))] Some(c) => Ok(c), - #[cfg(feature = "tokio")] + #[cfg(not(feature = "async-std"))] None => Err(ServerError::Killed), }; } From cd6153ecfffcd337b4b55451858feeccb23260d5 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 21:03:18 -0600 Subject: [PATCH 25/75] chore: Fix features --- Cargo.toml | 2 +- src/connection/mod.rs | 12 ++++++------ src/connection/queue/send.rs | 4 ++-- src/server/mod.rs | 14 +++++++------- src/util/mod.rs | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09a6e64..9a7085d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Bavfalcon9 "] edition = "2021" [features] -default = [ "async-std" ] +default = [ "async_std" ] mcpe = [] debug = [] debug_all = [] diff --git a/src/connection/mod.rs b/src/connection/mod.rs index c761320..75593e3 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -12,7 +12,7 @@ use std::{ time::{Duration, SystemTime}, }; -#[cfg(feature = "async-std")] +#[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, Sender}, net::UdpSocket, @@ -20,7 +20,7 @@ use async_std::{ task::{self, sleep, JoinHandle}, }; use binary_utils::Streamable; -#[cfg(feature = "tokio")] +#[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, sync::{ @@ -294,11 +294,11 @@ impl Connection { } match net.recv().await { - #[cfg(feature = "async-std")] + #[cfg(feature = "async_std")] Ok(payload) => { handle_payload!(payload); } - #[cfg(feature = "tokio")] + #[cfg(feature = "async_tokio")] Some(payload) => { handle_payload!(payload); } @@ -427,9 +427,9 @@ impl Connection { let tasks = self.tasks.clone(); for task in tasks.lock().await.drain(..) { - #[cfg(feature = "async-std")] + #[cfg(feature = "async_std")] task.cancel().await; - #[cfg(feature = "tokio")] + #[cfg(feature = "async_tokio")] task.abort(); } } diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 85d5684..c1004c3 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -2,10 +2,10 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; -#[cfg(feature = "async-std")] +#[cfg(feature = "async_std")] use async_std::net::UdpSocket; use binary_utils::Streamable; -#[cfg(feature = "tokio")] +#[cfg(feature = "async_tokio")] use tokio::net::UdpSocket; use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; diff --git a/src/server/mod.rs b/src/server/mod.rs index e11c660..377999f 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use std::sync::atomic::AtomicBool; use std::{net::SocketAddr, sync::Arc}; -#[cfg(feature = "async-std")] +#[cfg(feature = "async_std")] use async_std::{ channel::unbounded, channel::TrySendError, @@ -17,10 +17,10 @@ use async_std::{ task::{self}, }; use binary_utils::Streamable; -#[cfg(feature = "async-std")] +#[cfg(feature = "async_std")] use futures::select; -#[cfg(feature = "tokio")] +#[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, sync::mpsc::channel as bounded, @@ -435,13 +435,13 @@ impl Listener { } else { let receiver = self.recv_comm.recv().await; return match receiver { - #[cfg(feature = "async-std")] + #[cfg(feature = "async_std")] Ok(c) => Ok(c), - #[cfg(feature = "async-std")] + #[cfg(feature = "async_std")] Err(_) => Err(ServerError::Killed), - #[cfg(not(feature = "async-std"))] + #[cfg(feature = "async_tokio")] Some(c) => Ok(c), - #[cfg(not(feature = "async-std"))] + #[cfg(feature = "async_tokio")] None => Err(ServerError::Killed), }; } diff --git a/src/util/mod.rs b/src/util/mod.rs index 92268e1..2d9514c 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,8 @@ -#[cfg(feature = "async-std")] +#[cfg(feature = "async_std")] use async_std::task::sleep as async_sleep; use std::net::{SocketAddr, ToSocketAddrs}; use std::{collections::HashMap, time::SystemTime}; -#[cfg(feature = "tokio")] +#[cfg(feature = "async_tokio")] use tokio::time::sleep as async_sleep; pub(crate) mod debug; From c60b39e5221703760d257fcc5f7441dfb67d3ebc Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 8 Jan 2023 21:04:05 -0600 Subject: [PATCH 26/75] chore: Update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7ebc7fa..afe934c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A fully functional RakNet implementation in rust, asynchronously driven. +To use `tokio` you must enable the `tokio` feature, and use `disable-default-features` to disable the default `async-std` feature. + ```rust // Create a server use rakrs::Listener; From 1ac49662ec7f4470bbb2e96157368d33641e6416 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Wed, 11 Jan 2023 00:55:10 -0600 Subject: [PATCH 27/75] feat: Finishing up for a release? --- Cargo.toml | 2 +- src/connection/mod.rs | 188 ++++++++++++++++++++++---------- src/connection/queue/mod.rs | 23 ++++ src/connection/queue/recv.rs | 8 +- src/connection/queue/send.rs | 205 +++++++++++++++++++++++++++++++---- src/protocol/ack.rs | 29 +++++ src/server/mod.rs | 12 +- 7 files changed, 384 insertions(+), 83 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a7085d..756afe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rakrs" -version = "0.3.0-rc.2" +version = "0.3.0-alpha.1" authors = ["Bavfalcon9 "] edition = "2021" diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 75593e3..2dff0d2 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -4,17 +4,17 @@ pub mod queue; pub mod state; use std::{ - net::SocketAddr, + net::{IpAddr, Ipv4Addr, SocketAddr}, sync::{ atomic::{AtomicBool, AtomicU64}, Arc, }, - time::{Duration, SystemTime}, + time::Duration, }; #[cfg(feature = "async_std")] use async_std::{ - channel::{bounded, Receiver, Sender}, + channel::{bounded, Receiver, RecvError, Sender}, net::UdpSocket, sync::{Mutex, RwLock}, task::{self, sleep, JoinHandle}, @@ -32,13 +32,15 @@ use tokio::{ }; use crate::{ + error::connection::ConnectionError, protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, packet::{ - online::{ConnectedPong, OnlinePacket}, + online::{ConnectedPing, ConnectedPong, ConnectionAccept, OnlinePacket}, Packet, }, + reliability::Reliability, }, rakrs_debug, server::current_epoch, @@ -135,7 +137,7 @@ impl Connection { let tk = c.tasks.clone(); let mut tasks = tk.lock().await; - tasks.push(c.init_tick(socket)); + tasks.push(c.init_tick()); tasks.push(c.init_net_recv(socket, net, net_sender).await); // todo finish the send queue @@ -146,10 +148,14 @@ impl Connection { } /// Initializes the client ticking process! - pub fn init_tick(&self, socket: &Arc) -> task::JoinHandle<()> { + pub fn init_tick(&self) -> task::JoinHandle<()> { let address = self.address; let closer = self.disconnect.clone(); let last_recv = self.recv_time.clone(); + let send_queue = self.send_queue.clone(); + let recv_queue = self.recv_queue.clone(); + let state = self.state.clone(); + let mut last_ping: u16 = 0; // initialize the event io // we initialize the ticking function here, it's purpose is to update the state of the current connection @@ -157,8 +163,10 @@ impl Connection { return task::spawn(async move { loop { sleep(Duration::from_millis(50)).await; + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut cstate = state.lock().await; - if last_recv.load(std::sync::atomic::Ordering::Relaxed) + 20 >= current_epoch() { + if recv + 20000 <= current_epoch() { rakrs_debug!( true, "[{}] Connection has been closed due to inactivity!", @@ -168,6 +176,39 @@ impl Connection { closer.store(true, std::sync::atomic::Ordering::Relaxed); break; } + + if recv + 15000 <= current_epoch() && cstate.is_reliable() { + *cstate = ConnectionState::TimingOut; + rakrs_debug!( + true, + "[{}] Connection is timing out, sending a ping!", + to_address_token(address) + ); + } + + let mut sendq = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; + + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = sendq + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {}; + last_ping = 0; + } else { + last_ping += 50; + } + + sendq.update().await; + + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if let Ok(p) = ack.parse() { + sendq.send_stream(&p).await; + } } }); } @@ -177,7 +218,7 @@ impl Connection { pub async fn init_net_recv( &self, socket: &Arc, - mut net: Receiver>, + net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); @@ -197,17 +238,22 @@ impl Connection { ); break; } - - rakrs_debug!( - true, - "[{}] Waiting for packet...", - to_address_token(address) - ); - macro_rules! handle_payload { ($payload: ident) => { // We've recieved a payload! recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + let mut cstate = state.lock().await; + + if *cstate == ConnectionState::TimingOut { + rakrs_debug!( + true, + "[{}] Connection is no longer timing out!", + to_address_token(address) + ); + *cstate = ConnectionState::Connected; + } + + drop(cstate); let id = $payload[0]; match id { @@ -235,11 +281,12 @@ impl Connection { break; } } - if let Err(_) = res { + if let Err(e) = res { rakrs_debug!( true, - "[{}] Failed to process packet!", - to_address_token(address) + "[{}] Failed to process packet: {:?}!", + to_address_token(address), + e ); }; } @@ -323,51 +370,77 @@ impl Connection { to_address_token(*address), packet ); - return match packet.get_online() { + match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { ping_time: pk.time, pong_time: current_epoch() as i64, }; - let q = send_q.write().await; - // q.insert_immediate(response); - Ok(false) + let mut q = send_q.write().await; + if let Ok(_) = q + .send_packet(response.into(), Reliability::Reliable, true) + .await + { + return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectedPong packet!", + to_address_token(*address) + ); + return Err(()); + } + } + OnlinePacket::ConnectedPong(pk) => { + // do nothing rn + return Ok(false); } OnlinePacket::ConnectionRequest(pk) => { - // let response = ConnectionAccept { - // system_index: 0, - // client_address: from_address_token(connection.address.clone()), - // internal_id: SocketAddr::new( - // IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), - // 19132, - // ), - // request_time: pk.time, - // timestamp: SystemTime::now() - // .duration_since(connection.start_time) - // .unwrap() - // .as_millis() as i64, - // }; - rakrs_debug!( - true, - "[{}] ConnectionRequest not implemented, disconnecting!", - to_address_token(*address) - ); - Ok(true) + let response = ConnectionAccept { + system_index: 0, + client_address: *address, + internal_id: SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), + 19132, + ), + request_time: pk.time, + timestamp: current_epoch() as i64, + }; + let mut q = send_q.write().await; + *state.lock().await = ConnectionState::Connecting; + if let Ok(_) = q + .send_packet(response.into(), Reliability::Reliable, true) + .await + { + return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectionAccept packet!", + to_address_token(*address) + ); + return Err(()); + } } OnlinePacket::Disconnect(_) => { // Disconnect the client immediately. // connection.disconnect("Client disconnected.", false); - Ok(false) + return Ok(true); } - OnlinePacket::NewConnection(_) => { - // connection.state = ConnectionState::Connected; - Ok(true) + OnlinePacket::LostConnection(_) => { + // Disconnect the client immediately. + // connection.disconnect("Client disconnected.", false); + return Ok(true); } - _ => { - sender.send(buffer.clone()).await.unwrap(); - Ok(true) + OnlinePacket::NewConnection(_) => { + *state.lock().await = ConnectionState::Connected; + return Ok(false); } + _ => {} }; + + sender.send(buffer.clone()).await.unwrap(); + return Ok(false); } else { *state.lock().await = ConnectionState::Disconnecting; rakrs_debug!( @@ -378,7 +451,18 @@ impl Connection { return Err(()); } } - Err(()) + + sender.send(buffer.clone()).await.unwrap(); + return Ok(false); + } + + /// Recieve a packet from the client. + pub async fn recv(&mut self) -> Result, RecvError> { + let q = self.internal_net_recv.as_ref().lock().await; + match q.recv().await { + Ok(packet) => Ok(packet), + Err(e) => Err(e), + } } // /// Handle a RakNet Event. These are sent as they happen. @@ -412,12 +496,6 @@ impl Connection { // } // } - /// Initializes the client tick. - pub async fn tick(&mut self) { - let sendq = self.send_queue.write().await; - // sendq.tick().await; - } - pub async fn close(&mut self) { rakrs_debug!( true, diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index fc2c985..2de9c24 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -12,6 +12,7 @@ use crate::protocol::reliability::Reliability; use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; use crate::server::current_epoch; +#[derive(Debug, Clone)] pub enum NetQueueError { /// The insertion failed for any given reason. InvalidInsertion, @@ -75,6 +76,13 @@ where self.queue.insert(seq, (current_epoch(), item)); } + pub fn get_all(&mut self) -> Vec<(u32, Item)> { + self.queue + .iter() + .map(|(seq, (_, item))| (*seq, item.clone())) + .collect::>() + } + pub fn flush_old(&mut self, threshold: u64) -> Vec { let old = self .queue @@ -143,6 +151,11 @@ where } } + pub fn next(&mut self) -> u32 { + self.window.0 = self.window.0.wrapping_add(1); + return self.window.0; + } + pub fn insert(&mut self, index: u32, item: Item) -> bool { if index < self.window.0 { return false; @@ -160,6 +173,16 @@ where true } + pub fn missing(&self) -> Vec { + let mut missing = Vec::new(); + for i in self.window.0..self.window.1 { + if !self.queue.contains_key(&i) { + missing.push(i); + } + } + missing + } + pub fn flush(&mut self) -> Vec { let mut items = Vec::::new(); while self.queue.contains_key(&self.window.0) { diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index 19bd0ba..abf20eb 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -19,8 +19,8 @@ pub enum RecvQueueError { #[derive(Debug, Clone)] pub struct RecvQueue { frag_queue: FragmentQueue, - window: ReliableWindow, - reliable_window: ReliableWindow, + pub(crate) window: ReliableWindow, + pub(crate) reliable_window: ReliableWindow, order_channels: HashMap>>, /// Set of sequences that we've acknowledged. /// (seq, time) @@ -58,6 +58,10 @@ impl RecvQueue { self.ready.drain(..).collect::>>() } + pub fn ack_flush(&mut self) -> Vec { + self.ack.drain().map(|(seq, _)| seq).collect() + } + fn handle_frame(&mut self, frame: &Frame) { if let Some(reliable_index) = frame.reliable_index { if !self.reliable_window.insert(reliable_index) { diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index c1004c3..145c004 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -12,10 +12,23 @@ use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; use crate::protocol::frame::{Frame, FramePacket}; use crate::protocol::packet::Packet; use crate::protocol::reliability::Reliability; -use crate::protocol::{RAKNET_HEADER_FRAME_OVERHEAD, RAKNET_HEADER_OVERHEAD}; -use crate::util::SafeGenerator; +use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; +use crate::rakrs_debug; +use crate::util::{to_address_token, SafeGenerator}; -use super::{FragmentQueue, NetQueue, OrderedQueue, RecoveryQueue}; +use super::{FragmentQueue, FragmentQueueError, NetQueue, RecoveryQueue}; + +#[derive(Debug, Clone)] +pub enum SendQueueError { + /// The packet is too large to be sent. + PacketTooLarge, + /// Parsing Error + ParseError, + /// Fragmentation error + FragmentError(FragmentQueueError), + /// Send queue error + SendError, +} /// This queue is used to prioritize packets being sent out /// Packets that are old, are either dropped or requested again. @@ -43,11 +56,14 @@ pub struct SendQueue { /// The current recovery queue. ack: RecoveryQueue, + ack_tries: Vec<(u32, u8)>, /// The fragment queue. fragment_queue: FragmentQueue, - order_channels: HashMap>>, + /// The ordered channels. + /// (send_seq, reliable_seq) + order_channels: HashMap, ready: Vec, @@ -71,6 +87,7 @@ impl SendQueue { send_seq: SafeGenerator::new(), reliable_seq: SafeGenerator::new(), ack: RecoveryQueue::new(), + ack_tries: Vec::new(), fragment_queue: FragmentQueue::new(), order_channels: HashMap::new(), ready: Vec::new(), @@ -86,29 +103,114 @@ impl SendQueue { &mut self, packet: Vec, reliability: Reliability, - channel: u8, immediate: bool, - ) { + channel: Option, + ) -> Result<(), SendQueueError> { let reliable = if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { Reliability::ReliableOrd } else { reliability }; - // match reliability { - // Reliability::Unreliable => { - // // we can just send this packet out immediately. - // let frame = Frame::new(Reliability::Unreliable, Some(packet)); - // self.send_frame(frame).await; - // }, - // Reliability::Reliable => { - // // we need to send this packet out reliably. - // let frame = Frame::new(Reliability::Reliable, Some(packet)); - // self.send_frame(frame).await; - // }, - // } + match reliability { + Reliability::Unreliable => { + // we can just send this packet out immediately. + let frame = Frame::new(Reliability::Unreliable, Some(packet)); + self.send_frame(frame).await; + return Ok(()); + } + Reliability::Reliable => { + // we need to send this packet out reliably. + let frame = Frame::new(Reliability::Reliable, Some(packet)); + self.send_frame(frame).await; + return Ok(()); + } + _ => {} + }; + + // do another integrity check + // this is to check to see if we really need to split this packet. + if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { + // we need to split this packet! + // pass the buffer to the fragment queue. + let mut pk = FramePacket::new(); + pk.sequence = self.send_seq.next(); + pk.reliability = reliability; + + let fragmented = self.fragment_queue.split_insert(&packet, self.mtu_size); + + if fragmented.is_ok() { + let frag_id = fragmented.unwrap(); + let (_, frames) = self.fragment_queue.get_mut(&frag_id).unwrap(); + let (ord_seq, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *ord_index = ord_index.wrapping_add(1); + *ord_seq = ord_seq.wrapping_add(1); + + for frame in frames.iter_mut() { + frame.reliability = reliability; + frame.sequence_index = Some(*ord_seq); + frame.order_channel = Some(channel.unwrap_or(0)); + frame.order_index = Some(*ord_index); + + if frame.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + } + + // Add this frame packet to the recovery queue. + if let Ok(p) = pk.parse() { + self.send_stream(&p[..]).await; + self.ack.insert_id(pk.sequence, pk); + return Ok(()); + } else { + return Err(SendQueueError::SendError); + } + } else { + // we couldn't send this frame! + return Err(SendQueueError::FragmentError(fragmented.unwrap_err())); + } + } else { + // we're not gonna send this frame out yet! + // we need to wait for the next tick. + let mut frame = Frame::new(reliable, Some(packet)); + + if frame.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + + if frame.reliability.is_ordered() { + let (_, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *ord_index = ord_index.wrapping_add(1); + frame.order_index = Some(*ord_index); + frame.sequence_index = Some(self.send_seq.get()); + } else if frame.reliability.is_sequenced() { + let (seq_index, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *seq_index = seq_index.wrapping_add(1); + frame.order_index = Some(*ord_index); + frame.sequence_index = Some(*seq_index); + } + + if immediate { + self.send_frame(frame).await; + } else { + self.ready.push(frame); + } + + return Ok(()); + } } + /// A wrapper to send a single frame over the wire. + /// While also reliabily tracking it. async fn send_frame(&mut self, mut frame: Frame) { let mut pk = FramePacket::new(); pk.sequence = self.send_seq.next(); @@ -118,17 +220,72 @@ impl SendQueue { frame.reliable_index = Some(self.reliable_seq.next()); } + pk.frames.push(frame); + + if pk.reliability.is_reliable() { + // this seems redundant, but we need to insert the packet into the ACK queue + self.ack.insert_id(self.reliable_seq.get(), pk.clone()); + } + if let Ok(buf) = pk.parse() { - self.send_socket(&buf[..]).await; + self.send_stream(&buf[..]).await; } } - async fn send_socket(&mut self, packet: &[u8]) { - self.socket.send_to(packet, &self.address).await.unwrap(); + pub(crate) async fn send_stream(&mut self, packet: &[u8]) { + if let Err(_) = self.socket.send_to(packet, &self.address).await { + // we couldn't sent the packet! + } } - pub async fn send_packet(&mut self, packet: Packet, immediate: bool) { + pub async fn send_packet( + &mut self, + packet: Packet, + reliability: Reliability, + immediate: bool, + ) -> Result<(), SendQueueError> { // parse the packet + if let Ok(buf) = packet.parse() { + if let Err(e) = self.insert(buf, reliability, immediate, None).await { + rakrs_debug!( + true, + "[{}] Failed to insert packet into send queue: {:?}", + to_address_token(self.address), + e + ); + return Err(e); + } + return Ok(()); + } else { + return Err(SendQueueError::ParseError); + } + } + + pub async fn update(&mut self) { + // send all the ready packets + // todo batch these packets together + // todo by lengths + for frame in self.ready.drain(..).collect::>() { + self.send_frame(frame).await; + } + + // Flush ACK + // check to see if we need to resend any packets. + // todo actually implement this + let resend_queue = self.ack.flush().unwrap(); + // let mut resend_queue = Vec::::new(); + + // for (seq, packet) in self.ack.get_all() { + // if packet.resend_time < Instant::now() { + // resend_queue.push(packet.clone()); + // } + // } + + for packet in resend_queue.iter() { + if let Ok(buf) = packet.parse() { + self.send_stream(&buf[..]).await; + } + } } } @@ -144,11 +301,11 @@ impl Ackable for SendQueue { for record in ack.records.iter() { match record { Record::Single(SingleRecord { sequence }) => { - self.ack.remove(*sequence); + if let Ok(_) = self.ack.remove(*sequence) {}; } Record::Range(ranged) => { for i in ranged.start..ranged.end { - self.ack.remove(i); + if let Ok(_) = self.ack.remove(i) {}; } } } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 0bea23b..a1d47aa 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -77,6 +77,35 @@ impl Ack { .push(Record::Single(SingleRecord { sequence: seq })); } + pub fn from_records(missing: Vec, nack: bool) -> Self { + let mut records: Vec = Vec::new(); + let mut current: Range = 0..0; + + for m in missing { + if current.end + 1 == m { + current.end += 1; + } else if m > current.end { + // This is a new range. + records.push(Record::Range(RangeRecord { + start: current.start, + end: current.end, + })); + current.start = m; + current.end = m; + } else { + // This is a new single. + records.push(Record::Single(SingleRecord { sequence: m })); + current.start = m + 1; + current.end = m + 1; + } + } + + let mut nack = Self::new(records.len().try_into().unwrap(), nack); + nack.records = records; + + return nack; + } + pub fn from_missing(missing: Vec) -> Self { let mut records: Vec = Vec::new(); let mut current: Range = 0..0; diff --git a/src/server/mod.rs b/src/server/mod.rs index 377999f..ca7ce81 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -276,7 +276,7 @@ impl Listener { send_packet_to_socket(&socket, resp.into(), origin).await; continue; } - OfflinePacket::OpenConnectRequest(pk) => { + OfflinePacket::OpenConnectRequest(mut pk) => { // todo make a constant for this if !versions.contains(&pk.protocol) { let resp = IncompatibleProtocolVersion { @@ -298,6 +298,16 @@ impl Listener { pk.mtu_size ); + if pk.mtu_size > 2048 { + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", + to_address_token(*&origin), + pk.mtu_size + ); + pk.mtu_size = 2048; + } + let resp = OpenConnectReply { server_id, // todo allow encryption From 4610a4ded8ae1a1c651f8480e99ef08ae57718c9 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Wed, 11 Jan 2023 18:30:57 -0600 Subject: [PATCH 28/75] chore: fmt, try to fix a dumb bug on win10 --- .vscode/settings.json | 3 +- README.md | 12 +++++- src/connection/controller/mod.rs | 2 +- src/connection/controller/window.rs | 2 +- src/connection/mod.rs | 30 ++++++--------- src/connection/queue/mod.rs | 24 ++++++++---- src/connection/queue/recv.rs | 18 +++++---- src/connection/queue/send.rs | 27 ++++++++------ src/protocol/ack.rs | 52 +++++--------------------- src/protocol/frame.rs | 58 +---------------------------- src/protocol/mcpe/motd.rs | 4 +- src/server/mod.rs | 33 ++++++++-------- 12 files changed, 96 insertions(+), 169 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b01d07..e26bf4d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "deno.import_intellisense_origins": { "https://deno.land": true }, - "discord.enabled": true + "discord.enabled": true, + "discord-chat.token": "MjgxNTMwNzAyNTkwMjQ2OTE0.GzrCLJ.1oPmOTmbUgDpuma-HuguKPlMaw6j9QIUN7PCDU" } \ No newline at end of file diff --git a/README.md b/README.md index afe934c..a82b8b1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,15 @@ A fully functional RakNet implementation in rust, asynchronously driven. -To use `tokio` you must enable the `tokio` feature, and use `disable-default-features` to disable the default `async-std` feature. +### Installation + +By default `rakrs` will use `async_std` feature, which in turn, utilizes the `async_std` crate. To use `tokio` you will need to add it to cargo features as `async_tokio`. + +Using the tokio library with the `mcpe` feature may look like the following: + +```toml +rakrs = { version = "0.3.0", features = [ "mcpe", "async_std" ], default-features = false } +``` ```rust // Create a server @@ -34,7 +42,7 @@ async fn main() { server.start().await; loop { - let (conn, stream, _) = server.accept().await.unwrap(); + let conn = server.accept().await.unwrap(); // You can use the default handler, or create your own // the default handler diff --git a/src/connection/controller/mod.rs b/src/connection/controller/mod.rs index 18d4aa7..b8dfe29 100644 --- a/src/connection/controller/mod.rs +++ b/src/connection/controller/mod.rs @@ -1,4 +1,4 @@ -// todo +// TODO pub mod window; pub struct Controller { diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs index 2299e2a..15129ed 100644 --- a/src/connection/controller/window.rs +++ b/src/connection/controller/window.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::server::current_epoch; +use crate::{server::current_epoch, rakrs_debug}; #[derive(Debug, Clone)] pub struct ReliableWindow { diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 2dff0d2..a864c3f 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -32,7 +32,6 @@ use tokio::{ }; use crate::{ - error::connection::ConnectionError, protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, @@ -138,11 +137,7 @@ impl Connection { let tk = c.tasks.clone(); let mut tasks = tk.lock().await; tasks.push(c.init_tick()); - tasks.push(c.init_net_recv(socket, net, net_sender).await); - - // todo finish the send queue - // todo finish the ticking function - // todo add function for user to accept and send packets! + tasks.push(c.init_net_recv(net, net_sender).await); return c; } @@ -206,8 +201,10 @@ impl Connection { // Flush the queue of acks and nacks, and respond to them let ack = Ack::from_records(recv_q.ack_flush(), false); - if let Ok(p) = ack.parse() { - sendq.send_stream(&p).await; + if ack.records.len() > 0 { + if let Ok(p) = ack.parse() { + sendq.send_stream(&p).await; + } } } }); @@ -217,7 +214,6 @@ impl Connection { /// pub async fn init_net_recv( &self, - socket: &Arc, net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { @@ -246,7 +242,6 @@ impl Connection { if *cstate == ConnectionState::TimingOut { rakrs_debug!( - true, "[{}] Connection is no longer timing out!", to_address_token(address) ); @@ -283,7 +278,6 @@ impl Connection { } if let Err(e) = res { rakrs_debug!( - true, "[{}] Failed to process packet: {:?}!", to_address_token(address), e @@ -331,7 +325,6 @@ impl Connection { } _ => { rakrs_debug!( - true, "[{}] Unknown RakNet packet recieved (Or packet is sent out of scope).", to_address_token(address) ); @@ -364,12 +357,6 @@ impl Connection { ) -> Result { if let Ok(packet) = Packet::compose(buffer, &mut 0) { if packet.is_online() { - rakrs_debug!( - true, - "[{}] Recieved packet: {:?}", - to_address_token(*address), - packet - ); match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { @@ -391,8 +378,9 @@ impl Connection { return Err(()); } } - OnlinePacket::ConnectedPong(pk) => { + OnlinePacket::ConnectedPong(_pk) => { // do nothing rn + // TODO: add ping calculation return Ok(false); } OnlinePacket::ConnectionRequest(pk) => { @@ -496,6 +484,10 @@ impl Connection { // } // } + pub fn is_closed(&self) -> bool { + self.disconnect.load(std::sync::atomic::Ordering::Acquire) + } + pub async fn close(&mut self) { rakrs_debug!( true, diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index 2de9c24..035d0e4 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod send; pub use self::recv::*; pub use self::send::*; +use std::collections::BTreeMap; use std::collections::HashMap; use crate::protocol::frame::FragmentMeta; @@ -58,7 +59,7 @@ pub trait NetQueue { pub struct RecoveryQueue { /// The current queue of packets by timestamp /// (seq, (packet, timestamp)) - /// todo use the timestamp for round trip time (RTT) + // TODO use the timestamp for round trip time (RTT) queue: HashMap, } @@ -135,7 +136,7 @@ impl NetQueue for RecoveryQueue { pub struct OrderedQueue { /// The current ordered queue channels /// Channel, (Highest Index, Ord Index, Item) - pub queue: HashMap, + pub queue: BTreeMap, /// The window for this queue. pub window: (u32, u32), } @@ -146,7 +147,7 @@ where { pub fn new() -> Self { Self { - queue: HashMap::new(), + queue: BTreeMap::new(), window: (0, 0), } } @@ -173,6 +174,14 @@ where true } + pub fn insert_abs(&mut self, index: u32, item: Item) { + if index >= self.window.1 { + self.window.1 = index + 1; + } + + self.queue.insert(index, item); + } + pub fn missing(&self) -> Vec { let mut missing = Vec::new(); for i in self.window.0..self.window.1 { @@ -184,17 +193,18 @@ where } pub fn flush(&mut self) -> Vec { - let mut items = Vec::::new(); + let mut items = Vec::<(u32, Item)>::new(); while self.queue.contains_key(&self.window.0) { if let Some(item) = self.queue.remove(&self.window.0) { - items.push(item); + items.push((self.window.0, item)); } else { break; } self.window.0 = self.window.0.wrapping_add(1); } - return items; + items.sort_by(|a, b| a.0.cmp(&b.0)); + return items.iter().map(|(_, item)| item.clone()).collect::>(); } } @@ -413,8 +423,6 @@ impl FragmentQueue { /// This will split a given frame into a bunch of smaller frames within the specified /// restriction. pub fn split_insert(&mut self, buffer: &[u8], mtu: u16) -> Result { - let max_mtu = mtu - RAKNET_HEADER_FRAME_OVERHEAD; - self.fragment_id += self.fragment_id.wrapping_add(1); let id = self.fragment_id; diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index abf20eb..781cace 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -1,8 +1,6 @@ -use binary_utils::*; use std::collections::{HashMap, HashSet}; use crate::connection::controller::window::ReliableWindow; -use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; use crate::protocol::frame::{Frame, FramePacket}; use crate::protocol::reliability::Reliability; use crate::protocol::MAX_FRAGS; @@ -25,6 +23,7 @@ pub struct RecvQueue { /// Set of sequences that we've acknowledged. /// (seq, time) ack: HashSet<(u32, u64)>, + highest_seq: u32, ready: Vec>, } @@ -35,6 +34,7 @@ impl RecvQueue { ack: HashSet::new(), window: ReliableWindow::new(), reliable_window: ReliableWindow::new(), + highest_seq: 0, ready: Vec::new(), order_channels: HashMap::new(), } @@ -68,6 +68,8 @@ impl RecvQueue { return; } } + rakrs_debug!(true, "Handling frame: {:?}", frame); + if let Some(meta) = frame.fragment_meta.as_ref() { if meta.size > MAX_FRAGS { rakrs_debug!(true, "Fragment size is too large, rejected {}!", meta.size); @@ -96,12 +98,12 @@ impl RecvQueue { .entry(channel) .or_insert(OrderedQueue::new()); - if !queue.insert(frame.order_index.unwrap(), frame.body.clone()) { - return; - } - - for pk in queue.flush() { - self.ready.push(pk); + if queue.window.0 == frame.order_index.unwrap() { + for pk in queue.flush() { + self.ready.push(pk); + } + } else { + queue.insert_abs(frame.order_index.unwrap(), frame.body.clone()); } } _ => { diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 145c004..0d34002 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -39,11 +39,11 @@ pub struct SendQueue { /// The amount of time that needs to pass for a packet to be /// dropped or requested again. - timeout: u16, + _timeout: u16, /// The amount of times we should retry sending a packet before /// dropping it from the queue. This is currently set to `5`. - max_tries: u16, + _max_tries: u16, /// The current sequence number. This is incremented every time /// a packet is sent reliably. We can resend these if they are @@ -56,7 +56,6 @@ pub struct SendQueue { /// The current recovery queue. ack: RecoveryQueue, - ack_tries: Vec<(u32, u8)>, /// The fragment queue. fragment_queue: FragmentQueue, @@ -75,19 +74,18 @@ pub struct SendQueue { impl SendQueue { pub fn new( mtu_size: u16, - timeout: u16, - max_tries: u16, + _timeout: u16, + _max_tries: u16, socket: Arc, address: SocketAddr, ) -> Self { Self { mtu_size, - timeout, - max_tries, + _timeout, + _max_tries, send_seq: SafeGenerator::new(), reliable_seq: SafeGenerator::new(), ack: RecoveryQueue::new(), - ack_tries: Vec::new(), fragment_queue: FragmentQueue::new(), order_channels: HashMap::new(), ready: Vec::new(), @@ -263,15 +261,15 @@ impl SendQueue { pub async fn update(&mut self) { // send all the ready packets - // todo batch these packets together - // todo by lengths + // TODO batch these packets together + // TODO by lengths for frame in self.ready.drain(..).collect::>() { self.send_frame(frame).await; } // Flush ACK // check to see if we need to resend any packets. - // todo actually implement this + // TODO actually implement this let resend_queue = self.ack.flush().unwrap(); // let mut resend_queue = Vec::::new(); @@ -337,6 +335,13 @@ impl Ackable for SendQueue { } } + rakrs_debug!( + true, + "[{}] Resending {} packets", + to_address_token(self.address), + resend_queue.len() + ); + return resend_queue; } } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index a1d47aa..750e78d 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -11,12 +11,12 @@ pub(crate) trait Ackable { /// When an ack packet is recieved. /// We should ack the queue - fn ack(&mut self, index: Ack) {} + fn ack(&mut self, _: Ack) {} /// When an NACK packet is recieved. /// We should nack the queue /// This should return the packets that need to be resent. - fn nack(&mut self, packet: Ack) -> Vec { + fn nack(&mut self, _: Ack) -> Vec { todo!() } } @@ -45,9 +45,7 @@ impl RangeRecord { /// Fixes the end of the range if it is lower than the start. pub fn fix(&mut self) { if self.end < self.start { - let temp = self.end; - self.end = self.start; - self.start = temp; + std::mem::swap(&mut self.start, &mut self.end); } } } @@ -72,11 +70,6 @@ impl Ack { self.id == 0xa0 } - pub fn push_record(&mut self, seq: u32) { - self.records - .push(Record::Single(SingleRecord { sequence: seq })); - } - pub fn from_records(missing: Vec, nack: bool) -> Self { let mut records: Vec = Vec::new(); let mut current: Range = 0..0; @@ -86,10 +79,12 @@ impl Ack { current.end += 1; } else if m > current.end { // This is a new range. - records.push(Record::Range(RangeRecord { + let mut record = RangeRecord { start: current.start, end: current.end, - })); + }; + record.fix(); + records.push(Record::Range(record)); current.start = m; current.end = m; } else { @@ -105,35 +100,6 @@ impl Ack { return nack; } - - pub fn from_missing(missing: Vec) -> Self { - let mut records: Vec = Vec::new(); - let mut current: Range = 0..0; - - for m in missing { - if current.end + 1 == m { - current.end += 1; - } else if m > current.end { - // This is a new range. - records.push(Record::Range(RangeRecord { - start: current.start, - end: current.end, - })); - current.start = m; - current.end = m; - } else { - // This is a new single. - records.push(Record::Single(SingleRecord { sequence: m })); - current.start = m + 1; - current.end = m + 1; - } - } - - let mut nack = Self::new(records.len().try_into().unwrap(), true); - nack.records = records; - - return nack; - } } impl Streamable for Ack { @@ -174,11 +140,13 @@ impl Streamable for Ack { records.push(Record::Single(record)); } else { - let record: RangeRecord = RangeRecord { + let mut record: RangeRecord = RangeRecord { start: stream.read_u24::().unwrap(), end: stream.read_u24::().unwrap(), }; + record.fix(); + records.push(Record::Range(record)); } } diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 41283fc..3cb414b 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - io::{Cursor, Write}, -}; +use std::io::{Cursor, Write}; use binary_utils::error::BinaryError; use binary_utils::*; @@ -38,9 +35,6 @@ pub struct FramePacket { /// This is internal use only. pub(crate) reliability: Reliability, - - /// This is internal use only. - pub(crate) byte_length: usize, } impl FramePacket { @@ -50,56 +44,8 @@ impl FramePacket { sequence: 0, frames: Vec::new(), reliability: Reliability::ReliableOrd, - byte_length: 0, } } - - /// Paritions a stream into a bunch of fragments and returns a frame packet - /// that is partitioned, otherwise known as "fragmented". - /// This does not modify reliability. That is up to the caller. - pub fn partition(stream: Vec, id: u16, frag_size: u32) -> Vec { - let mut meta: FragmentMeta = FragmentMeta { - size: 0, - id, - index: 0, - }; - - let mut frames: Vec = Vec::new(); - let mut position: usize = 0; - - while position < stream.len() { - // check whether or not we can read the rest of of the stream - if stream[position..].len() < frag_size as usize { - // we can reliably read the rest of the buffer into a single frame. - let mut frame = Frame::init(); - frame.body = stream[position..].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - break; - } else { - // we can't read the rest of the stream into a single frame - // continue to split into multiple frames. - let mut frame = Frame::init(); - let to_pos = position + (frag_size as usize); - frame.body = stream[position..to_pos].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - position = to_pos; - meta.index += 1; - } - } - - meta.size = frames.len() as u32; - - // Let's fix up the meta data in each frame. - for frame in frames.iter_mut() { - if let Some(m) = frame.fragment_meta.as_mut() { - m.size = meta.size; - } - } - - return frames; - } } impl Streamable for FramePacket { @@ -117,7 +63,6 @@ impl Streamable for FramePacket { reliability: Reliability::ReliableOrd, sequence, frames, - byte_length: 0, }); } @@ -126,7 +71,6 @@ impl Streamable for FramePacket { reliability: Reliability::ReliableOrd, sequence, frames, - byte_length: 0, }); } diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index 0833401..fac88f9 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -53,7 +53,7 @@ pub struct Motd { /// The server's port pub port: String, /// The IPv6 port - /// TODO: Implement this + // TODO: Implement this pub ipv6_port: String, } @@ -86,7 +86,7 @@ impl Motd { "Netrex".to_string(), self.gamemode.as_str().to_string(), "1".to_string(), - // Todo: Figure out why this is not working + // TODO: Figure out why this is not working // self.gamemode.to_string(), self.port.to_string(), self.ipv6_port.to_string(), diff --git a/src/server/mod.rs b/src/server/mod.rs index ca7ce81..58667be 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,4 @@ +#[allow(unused)] /// Server events module. Handles things like updating the MOTD /// for certain connections. This is a notifier channel. pub mod event; @@ -8,17 +9,12 @@ use std::{net::SocketAddr, sync::Arc}; #[cfg(feature = "async_std")] use async_std::{ - channel::unbounded, - channel::TrySendError, channel::{bounded, Receiver, Sender}, - io::timeout, net::UdpSocket, - sync::{Condvar, Mutex}, + sync::Mutex, task::{self}, }; use binary_utils::Streamable; -#[cfg(feature = "async_std")] -use futures::select; #[cfg(feature = "async_tokio")] use tokio::{ @@ -35,15 +31,11 @@ use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, }; -use crate::protocol::packet::online::Disconnect; use crate::protocol::packet::{Packet, Payload}; use crate::protocol::Magic; use crate::rakrs_debug; -use crate::server::event::ServerEventResponse; use crate::util::to_address_token; -use self::event::ServerEvent; - pub type Session = (ConnMeta, Sender>); // stupid hack for easier syntax :) @@ -109,10 +101,10 @@ pub struct Listener { /// It allows you to use the syntax sugar for `Listener::accept()`. recv_comm: Receiver, send_comm: Sender, - // todo, fix this! + // TODO, fix this! // send_evnt: Sender<(ServerEvent, oneshot::Sender)>, // pub recv_evnt: Arc)>>>, - // todo + // TODO /// A Notifier (sephamore) that will wait until all notified listeners /// are completed, and finish closing. closed: Arc, @@ -147,7 +139,7 @@ impl Listener { let (send_comm, recv_comm) = bounded::(10); // This channel is responsible for handling and dispatching events between clients. // Oneshot will garauntee this event is intended for the client whom requested the event. - // todo: Fix with new event system + // TODO: Fix with new event system // let (send_evnt, recv_evnt) = // mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); @@ -204,6 +196,12 @@ impl Listener { let motd_default = default_motd.clone(); loop { + // TODO: This may be fixable by killing the task itself. + if closer.load(std::sync::atomic::Ordering::Relaxed) { + rakrs_debug!("Server is closing, stopping recieve loop"); + break; + } + let length: usize; let origin: SocketAddr; @@ -233,7 +231,8 @@ impl Listener { OfflinePacket::UnconnectedPing(_) => { // let (resp_tx, resp_rx) = // oneshot::channel::(); - let mut motd: Motd = motd_default.clone(); + #[cfg(feature = "mcpe")] + let motd: Motd = motd_default.clone(); // if let Err(e) = send_evt.try_send(( // ServerEvent::RefreshMotdRequest(origin, motd.clone()), @@ -277,7 +276,7 @@ impl Listener { continue; } OfflinePacket::OpenConnectRequest(mut pk) => { - // todo make a constant for this + // TODO make a constant for this if !versions.contains(&pk.protocol) { let resp = IncompatibleProtocolVersion { protocol: pk.protocol, @@ -310,10 +309,10 @@ impl Listener { let resp = OpenConnectReply { server_id, - // todo allow encryption + // TODO allow encryption security: false, magic: Magic::new(), - // todo make this configurable, this is sent to the client to change + // TODO make this configurable, this is sent to the client to change // it's mtu size, right now we're using what the client prefers. // however in some cases this may not be the preferred use case, for instance // on servers with larger worlds, you may want a larger mtu size, or if From 9a6144e58ce012072c6d67b619f31616b7909828 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Wed, 11 Jan 2023 20:44:48 -0600 Subject: [PATCH 29/75] chore: locales --- src/connection/queue/recv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index 781cace..0996ab7 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -67,8 +67,8 @@ impl RecvQueue { if !self.reliable_window.insert(reliable_index) { return; } + rakrs_debug!(true, "Handling frame: {:?}", frame); } - rakrs_debug!(true, "Handling frame: {:?}", frame); if let Some(meta) = frame.fragment_meta.as_ref() { if meta.size > MAX_FRAGS { From 4c17c60e533102643503fc36293739d46b44fe16 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Wed, 11 Jan 2023 22:51:25 -0600 Subject: [PATCH 30/75] chore: locales --- src/connection/controller/window.rs | 6 ++- src/connection/mod.rs | 59 +++++++++++++++++++++++------ src/connection/queue/recv.rs | 45 ++++++++++++++++++++-- src/connection/queue/send.rs | 17 ++++----- src/protocol/ack.rs | 48 ++++++++++++++++------- 5 files changed, 135 insertions(+), 40 deletions(-) diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs index 15129ed..c4bb76e 100644 --- a/src/connection/controller/window.rs +++ b/src/connection/controller/window.rs @@ -59,7 +59,7 @@ impl ReliableWindow { } /// Returns all the packets that are in the window. - pub fn missing(&mut self) -> Vec { + pub fn missing(&self) -> Vec { let mut missing = Vec::new(); for i in self.window.0..self.window.1 { @@ -71,6 +71,10 @@ impl ReliableWindow { missing } + pub fn range(&self) -> (u32, u32) { + self.window + } + /// Forcefully clears packets that are not in the window. /// This is used when the window is too small to fit all the packets. pub fn clear_outdated(&mut self) { diff --git a/src/connection/mod.rs b/src/connection/mod.rs index a864c3f..4ce1aaf 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -47,7 +47,7 @@ use crate::{ }; use self::{ - queue::{RecvQueue, SendQueue}, + queue::{RecvQueue, SendQueue, SendQueueError}, state::ConnectionState, }; pub(crate) type ConnNetChan = Arc>>>; @@ -161,7 +161,19 @@ impl Connection { let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); let mut cstate = state.lock().await; + if *cstate == ConnectionState::Disconnected { + rakrs_debug!( + true, + "[{}] Connection has been closed due to state!", + to_address_token(address) + ); + // closer.notify_all(); + closer.store(true, std::sync::atomic::Ordering::Relaxed); + break; + } + if recv + 20000 <= current_epoch() { + *cstate = ConnectionState::Disconnected; rakrs_debug!( true, "[{}] Connection has been closed due to inactivity!", @@ -206,6 +218,14 @@ impl Connection { sendq.send_stream(&p).await; } } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.parse() { + sendq.send_stream(&p).await; + } + } } }); } @@ -295,21 +315,24 @@ impl Connection { // We should resend them. let mut sq = send_q.write().await; let resend = sq.nack(nack); - for packet in resend { - if let Ok(buffer) = packet.parse() { - if let Err(_) = sender.send(buffer).await { + + if resend.len() > 0 { + for packet in resend { + if let Ok(buffer) = packet.parse() { + if let Err(_) = sq.insert(buffer, Reliability::Unreliable, true, Some(0)).await { + rakrs_debug!( + true, + "[{}] Failed to insert packet into send queue!", + to_address_token(address) + ); + } + } else { rakrs_debug!( true, - "[{}] Failed to send packet to client!", + "[{}] Failed to send packet to client (parsing failed)!", to_address_token(address) ); } - } else { - rakrs_debug!( - true, - "[{}] Failed to send packet to client (parsing failed)!", - to_address_token(address) - ); } } } @@ -320,7 +343,9 @@ impl Connection { // The client acknowledges it recieved these packets // We should remove them from the queue. let mut sq = send_q.write().await; - sq.ack(ack); + sq.ack(ack.clone()); + drop(sq); + recv_q.lock().await.ack(ack); } } _ => { @@ -488,6 +513,16 @@ impl Connection { self.disconnect.load(std::sync::atomic::Ordering::Acquire) } + /// Send a packet to the client. + /// These will be sent next tick unless otherwise specified. + pub async fn send(&mut self, buffer: Vec, immediate: bool) -> Result<(), SendQueueError> { + let mut q = self.send_queue.write().await; + if let Err(e) = q.insert(buffer, Reliability::ReliableOrd, immediate, Some(0)).await { + return Err(e); + } + Ok(()) + } + pub async fn close(&mut self) { rakrs_debug!( true, diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index 0996ab7..a77423e 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use crate::connection::controller::window::ReliableWindow; +use crate::protocol::ack::{Ackable, Ack, SingleRecord, Record}; use crate::protocol::frame::{Frame, FramePacket}; use crate::protocol::reliability::Reliability; use crate::protocol::MAX_FRAGS; @@ -23,6 +24,7 @@ pub struct RecvQueue { /// Set of sequences that we've acknowledged. /// (seq, time) ack: HashSet<(u32, u64)>, + nack: HashSet, highest_seq: u32, ready: Vec>, } @@ -32,6 +34,7 @@ impl RecvQueue { Self { frag_queue: FragmentQueue::new(), ack: HashSet::new(), + nack: HashSet::new(), window: ReliableWindow::new(), reliable_window: ReliableWindow::new(), highest_seq: 0, @@ -45,6 +48,12 @@ impl RecvQueue { return Err(RecvQueueError::OldSeq); } + if self.window.range().0 < packet.sequence { + for i in self.window.range().0..packet.sequence { + self.nack.insert(i); + } + } + self.ack.insert((packet.sequence, current_epoch())); for frame in packet.frames.iter() { @@ -62,12 +71,15 @@ impl RecvQueue { self.ack.drain().map(|(seq, _)| seq).collect() } + pub fn nack_queue(&mut self) -> Vec { + self.nack.iter().map(|x| *x).collect::>() + } + fn handle_frame(&mut self, frame: &Frame) { if let Some(reliable_index) = frame.reliable_index { if !self.reliable_window.insert(reliable_index) { return; } - rakrs_debug!(true, "Handling frame: {:?}", frame); } if let Some(meta) = frame.fragment_meta.as_ref() { @@ -81,9 +93,12 @@ impl RecvQueue { if let Ok(data) = res { // reconstructed frame packet! self.ready.push(data); + } else { + rakrs_debug!(true, "Still Missing some fragments! {:?}", frame.fragment_meta.as_ref().unwrap()); } return; } + match frame.reliability { Reliability::Unreliable => { self.ready.push(frame.body.clone()); @@ -98,12 +113,10 @@ impl RecvQueue { .entry(channel) .or_insert(OrderedQueue::new()); - if queue.window.0 == frame.order_index.unwrap() { + if queue.insert(frame.order_index.unwrap(), frame.body.clone()) { for pk in queue.flush() { self.ready.push(pk); } - } else { - queue.insert_abs(frame.order_index.unwrap(), frame.body.clone()); } } _ => { @@ -112,3 +125,27 @@ impl RecvQueue { } } } + +impl Ackable for RecvQueue { + type NackItem = (); + + fn ack(&mut self, ack: Ack) { + if ack.is_nack() { + return; + } + + // these packets are acknowledged, so we can remove them from the queue. + for record in ack.records.iter() { + match record { + Record::Single(SingleRecord { sequence }) => { + self.nack.remove(&sequence); + } + Record::Range(ranged) => { + for i in ranged.start..ranged.end { + self.nack.remove(&i); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 0d34002..1808e92 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -231,8 +231,14 @@ impl SendQueue { } pub(crate) async fn send_stream(&mut self, packet: &[u8]) { - if let Err(_) = self.socket.send_to(packet, &self.address).await { + if let Err(e) = self.socket.send_to(packet, &self.address).await { // we couldn't sent the packet! + rakrs_debug!( + true, + "[{}] Failed to send packet! {:?}", + to_address_token(self.address), + e + ); } } @@ -334,14 +340,7 @@ impl Ackable for SendQueue { } } } - - rakrs_debug!( - true, - "[{}] Resending {} packets", - to_address_token(self.address), - resend_queue.len() - ); - + return resend_queue; } } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 750e78d..dd69f26 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -6,6 +6,8 @@ use std::{io::Cursor, ops::Range}; use binary_utils::Streamable; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; +use crate::rakrs_debug; + pub(crate) trait Ackable { type NackItem; @@ -70,31 +72,49 @@ impl Ack { self.id == 0xa0 } - pub fn from_records(missing: Vec, nack: bool) -> Self { + pub fn from_records(mut missing: Vec, nack: bool) -> Self { let mut records: Vec = Vec::new(); let mut current: Range = 0..0; + missing.sort(); for m in missing { - if current.end + 1 == m { - current.end += 1; - } else if m > current.end { - // This is a new range. - let mut record = RangeRecord { - start: current.start, - end: current.end, - }; - record.fix(); - records.push(Record::Range(record)); + // if the current range is empty, set the start to the current missing + if m > current.start && current.start == 0 { current.start = m; current.end = m; + continue; + } + if m == current.end + 1 { + current.end = m; + continue; } else { - // This is a new single. + // end of range + records.push(Record::Range(RangeRecord { + start: current.start, + end: current.end, + })); + current.start = 0; + current.end = 0; + + // we also need to add the current missing to the records records.push(Record::Single(SingleRecord { sequence: m })); - current.start = m + 1; - current.end = m + 1; } } + if current.start == current.end { + if current.start == 0 { + return Self::new(0, false); + } + records.push(Record::Single(SingleRecord { + sequence: current.start, + })); + } else if current.start != 0 && current.end != 0 { + records.push(Record::Range(RangeRecord { + start: current.start, + end: current.end, + })); + } + let mut nack = Self::new(records.len().try_into().unwrap(), nack); nack.records = records; From c96e26f0f889ff9aafb5f70f6ab650aa719a8f62 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Wed, 11 Jan 2023 22:51:34 -0600 Subject: [PATCH 31/75] chore: fmt --- src/connection/controller/window.rs | 2 +- src/connection/mod.rs | 5 ++++- src/connection/queue/mod.rs | 5 ++++- src/connection/queue/recv.rs | 10 +++++++--- src/connection/queue/send.rs | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs index c4bb76e..29300e2 100644 --- a/src/connection/controller/window.rs +++ b/src/connection/controller/window.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{server::current_epoch, rakrs_debug}; +use crate::{rakrs_debug, server::current_epoch}; #[derive(Debug, Clone)] pub struct ReliableWindow { diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 4ce1aaf..a86f4f8 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -517,7 +517,10 @@ impl Connection { /// These will be sent next tick unless otherwise specified. pub async fn send(&mut self, buffer: Vec, immediate: bool) -> Result<(), SendQueueError> { let mut q = self.send_queue.write().await; - if let Err(e) = q.insert(buffer, Reliability::ReliableOrd, immediate, Some(0)).await { + if let Err(e) = q + .insert(buffer, Reliability::ReliableOrd, immediate, Some(0)) + .await + { return Err(e); } Ok(()) diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index 035d0e4..599a5ef 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -204,7 +204,10 @@ where } items.sort_by(|a, b| a.0.cmp(&b.0)); - return items.iter().map(|(_, item)| item.clone()).collect::>(); + return items + .iter() + .map(|(_, item)| item.clone()) + .collect::>(); } } diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index a77423e..b8eb2f8 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use crate::connection::controller::window::ReliableWindow; -use crate::protocol::ack::{Ackable, Ack, SingleRecord, Record}; +use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; use crate::protocol::frame::{Frame, FramePacket}; use crate::protocol::reliability::Reliability; use crate::protocol::MAX_FRAGS; @@ -94,7 +94,11 @@ impl RecvQueue { // reconstructed frame packet! self.ready.push(data); } else { - rakrs_debug!(true, "Still Missing some fragments! {:?}", frame.fragment_meta.as_ref().unwrap()); + rakrs_debug!( + true, + "Still Missing some fragments! {:?}", + frame.fragment_meta.as_ref().unwrap() + ); } return; } @@ -148,4 +152,4 @@ impl Ackable for RecvQueue { } } } -} \ No newline at end of file +} diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 1808e92..f72f30d 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -340,7 +340,7 @@ impl Ackable for SendQueue { } } } - + return resend_queue; } } From 38cb9f51e28662b38324e1004a30f31006935b6a Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 14 Jan 2023 12:35:30 -0600 Subject: [PATCH 32/75] chore: Fix tests --- src/connection/queue/mod.rs | 147 ++++++------------------------------ 1 file changed, 24 insertions(+), 123 deletions(-) diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index 599a5ef..d209497 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -132,6 +132,30 @@ impl NetQueue for RecoveryQueue { } } +/// An ordered queue is used to Index incoming packets over a channel +/// within a reliable window time. +/// +/// Usage: +/// ```rust +/// use rakrs::connection::queue::OrderedQueue; +/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); +/// // Insert a packet with the id of "1" +/// ord_qu.insert(1, vec![0, 1]); +/// ord_qu.insert(5, vec![1, 0]); +/// ord_qu.insert(3, vec![2, 0]); +/// +/// // Get the packets we still need. +/// let needed: Vec = ord_qu.missing(); +/// assert_eq!(needed, vec![0, 2, 4]); +/// +/// // We would in theory, request these packets, but we're going to insert them +/// ord_qu.insert(4, vec![2, 0, 0, 1]); +/// ord_qu.insert(2, vec![1, 0, 0, 2]); +/// +/// // Now let's return our packets in order. +/// // Will return a vector of these packets in order by their "id". +/// let ordered: Vec> = ord_qu.flush(); +/// ``` #[derive(Debug, Clone)] pub struct OrderedQueue { /// The current ordered queue channels @@ -211,129 +235,6 @@ where } } -/// An ordered queue is used to Index incoming packets over a channel -/// within a reliable window time. -/// -/// Usage: -/// ```rust -/// use rakrs::conn::queue::OrderedQueue; -/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); -/// // Insert a packet with the id of "1" -/// ord_qu.insert(vec![0, 1], 1); -/// ord_qu.insert(vec![1, 0], 5); -/// ord_qu.insert(vec![2, 0], 3); -/// -/// // Get the packets we still need. -/// let needed: Vec = ord_qu.flush_missing(); -/// assert_eq!(needed, vec![0, 2, 4]); -/// -/// // We would in theory, request these packets, but we're going to insert them -/// ord_qu.insert(vec![2, 0, 0, 1], 4); -/// ord_qu.insert(vec![1, 0, 0, 2], 2); -/// -/// // Now let's return our packets in order. -/// // Will return a vector of these packets in order by their "id". -/// let ordered: Vec> = ord_qu.flush(); -/// ``` -#[derive(Debug)] -pub struct OrderedQueueOld { - /// The queue of packets that are in order. Mapped to the time they were received. - queue: HashMap, - /// The current starting scope for the queue. - /// A start scope or "window start" is the range of packets that we are currently allowing. - /// Older packets will be ignored simply because they are old. - scope: (u32, u32), -} - -impl Clone for OrderedQueueOld -where - T: Clone, -{ - fn clone(&self) -> Self { - OrderedQueueOld { - queue: self.queue.clone(), - scope: self.scope.clone(), - } - } -} - -impl OrderedQueueOld -where - T: Sized + Clone, -{ - pub fn new() -> Self { - Self { - queue: HashMap::new(), - scope: (0, 0), - } - } - - /// Inserts the given packet into the queue. - /// This will return `false` if the packet is out of scope. - pub fn insert(&mut self, packet: T, id: u32) -> bool { - // if the packet id is lower than our scope, ignore it - // this packet is way to old for us to handle. - if id < self.scope.0 { - return false; - } - - // If the packet is higher than our current scope, we need to adjust our scope. - // This is because we are now allowing packets that are newer than our current scope. - if id > self.scope.1 { - self.scope.1 = id + 1; - } - - self.queue.insert(id, packet); - return true; - } - - /// Drains the current queue by removing all packets from the queue. - /// This will return the packets in order only if they were within the current scope. - /// This method will also update the scope and adjust it to the newest window. - pub fn flush(&mut self) -> Vec { - // clear all packets not within our scope - self.clear_out_of_scope(); - - // now drain the queue - let mut map = HashMap::new(); - std::mem::swap(&mut map, &mut self.queue); - - let mut clean = map.iter().collect::>(); - clean.sort_by_key(|m| m.0); - - return clean.iter().map(|m| m.1.clone()).collect::>(); - } - - /// Clears all packets that are out of scope. - /// Returning only the ones that have not been recieved. - pub fn flush_missing(&mut self) -> Vec { - let mut missing: Vec = Vec::new(); - // we need to get the amount of ids that are missing from the queue. - for i in self.scope.0..self.scope.1 { - if !self.queue.contains_key(&i) { - missing.push(i); - } - } - - // we can safely update the scope - self.scope.0 = missing.get(0).unwrap_or(&self.scope.0).clone(); - return missing; - } - - fn clear_out_of_scope(&mut self) { - // clear all packets not within our current scope. - // this is done by removing all packets that are older than our current scope. - for (id, _) in self.queue.clone().iter() { - if *id < self.scope.0 { - self.queue.remove(id); - } - } - } - - pub fn get_scope(&self) -> u32 { - self.scope.1 - self.scope.0 - } -} /// A specialized structure for re-ordering fragments over the wire. /// You can use this structure to fragment frames as well. From 426747f64207fe0204eda50d2aae42d4082b91bd Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 14 Jan 2023 12:37:21 -0600 Subject: [PATCH 33/75] chore(fmt) --- src/connection/queue/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index d209497..390bccb 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -235,7 +235,6 @@ where } } - /// A specialized structure for re-ordering fragments over the wire. /// You can use this structure to fragment frames as well. /// From 162189e79bbdc119f809875a7f1a35ace1c9e7ea Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 14 Jan 2023 12:38:23 -0600 Subject: [PATCH 34/75] chore: Bump version [ci skip] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 756afe1..ca0fa7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rakrs" -version = "0.3.0-alpha.1" +version = "0.3.0-rc.3" authors = ["Bavfalcon9 "] edition = "2021" From c5ff04aefa790cb04c3dc90d9ee60a160c27ee69 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 14 Jan 2023 12:43:12 -0600 Subject: [PATCH 35/75] fuck you vscode --- .vscode/settings.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index e26bf4d..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "[typescript]": { - "editor.defaultFormatter": "denoland.vscode-deno", - }, - "deno.enable": true, - "deno.unstable": true, - "deno.import_intellisense_origins": { - "https://deno.land": true - }, - "discord.enabled": true, - "discord-chat.token": "MjgxNTMwNzAyNTkwMjQ2OTE0.GzrCLJ.1oPmOTmbUgDpuma-HuguKPlMaw6j9QIUN7PCDU" -} \ No newline at end of file From 27a21399d2a0e34dc2ffb5dc226112ecdd2aa505 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 14 Jan 2023 13:08:34 -0600 Subject: [PATCH 36/75] feat: Fix tokio --- Cargo.toml | 2 +- src/connection/mod.rs | 16 ++++++++++++++-- src/connection/queue/recv.rs | 2 -- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca0fa7d..8c43e99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rakrs" -version = "0.3.0-rc.3" +version = "0.3.0-rc.4" authors = ["Bavfalcon9 "] edition = "2021" diff --git a/src/connection/mod.rs b/src/connection/mod.rs index a86f4f8..75cdabf 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -30,6 +30,11 @@ use tokio::{ task::{self, JoinHandle}, time::sleep, }; +#[cfg(feature = "async_tokio")] +pub enum RecvError { + Closed, + Timeout, +} use crate::{ protocol::{ @@ -234,7 +239,7 @@ impl Connection { /// pub async fn init_net_recv( &self, - net: Receiver>, + mut net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); @@ -471,10 +476,17 @@ impl Connection { /// Recieve a packet from the client. pub async fn recv(&mut self) -> Result, RecvError> { - let q = self.internal_net_recv.as_ref().lock().await; + #[allow(unused_mut)] + let mut q = self.internal_net_recv.as_ref().lock().await; match q.recv().await { + #[cfg(feature = "async_std")] Ok(packet) => Ok(packet), + #[cfg(feature = "async_std")] Err(e) => Err(e), + #[cfg(feature = "async_tokio")] + Some(packet) => Ok(packet), + #[cfg(feature = "async_tokio")] + None => Err(RecvError::Closed), } } diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index b8eb2f8..48d5878 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -25,7 +25,6 @@ pub struct RecvQueue { /// (seq, time) ack: HashSet<(u32, u64)>, nack: HashSet, - highest_seq: u32, ready: Vec>, } @@ -37,7 +36,6 @@ impl RecvQueue { nack: HashSet::new(), window: ReliableWindow::new(), reliable_window: ReliableWindow::new(), - highest_seq: 0, ready: Vec::new(), order_channels: HashMap::new(), } From ddb4e7a06f14bfe63b388e274d9d9bdbe6b610dd Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 10 Jun 2023 22:09:16 -0500 Subject: [PATCH 37/75] feat: Client init [ci skip] --- src/client/mod.rs | 251 ++++++++++++++++++++++++++++++++++++++++ src/error/client.rs | 8 ++ src/error/connection.rs | 2 +- src/error/mod.rs | 1 + src/error/server.rs | 2 +- src/lib.rs | 1 + src/server/mod.rs | 11 ++ 7 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 src/client/mod.rs create mode 100644 src/error/client.rs diff --git a/src/client/mod.rs b/src/client/mod.rs new file mode 100644 index 0000000..8eaf4ad --- /dev/null +++ b/src/client/mod.rs @@ -0,0 +1,251 @@ +use std::{sync::{Arc, atomic::{AtomicBool, AtomicU64}}, net::SocketAddr, time::Duration}; + +#[cfg(feature = "async_std")] +use async_std::{ + channel::{bounded, Receiver, RecvError, Sender}, + net::UdpSocket, + sync::{Mutex, RwLock}, + task::{self, sleep, JoinHandle}, +}; +use binary_utils::Streamable; +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + sync::{ + mpsc::{channel as bounded, Receiver, Sender}, + Mutex, RwLock, + }, + task::{self, JoinHandle}, + time::sleep, +}; + +use crate::{ + connection::{queue::{RecvQueue, SendQueue, send}, state::ConnectionState}, + server::{PossiblySocketAddr, current_epoch}, error::client::ClientError, rakrs_debug, protocol::{packet::{Packet, online::ConnectedPing}, reliability::Reliability, ack::Ack}, +}; + +/// This struct is used to connect to RakNet servers. +/// To start a connection, use `Client::connect()`. +pub struct Client { + /// The connection state of the client. + pub(crate) state: Arc>, + /// The send queue is used internally to send packets to the server. + send_queue: Option>>, + /// The receive queue is used internally to receive packets from the server. + /// This is read from before sending + recv_queue: Arc>, + /// The network recieve channel is used to receive raw packets from the server. + network_recv: Arc>>>>, + /// The internal channel that is used to process packets + internal_recv: Arc>>>>, + internal_send: Arc>>>, + tasks: Arc>>>, + /// A notifier for when the client should kill threads. + closed: Arc, + /// A int for the last time a packet was received. + recv_time: Arc, + /// The maximum packet size that can be sent to the server. + mtu: u16, + /// The RakNet version of the client. + version: u8, +} + +impl Client { + /// Creates a new client. + /// > Note: This does not start a connection. You must use `Client::connect()` to start a connection. + pub fn new(version: u8, mtu: u16) -> Self { + Self { + state: Arc::new(Mutex::new(ConnectionState::Offline)), + send_queue: None, + recv_queue: Arc::new(Mutex::new(RecvQueue::new())), + network_recv: Arc::new(None), + mtu, + version, + tasks: Arc::new(Mutex::new(Vec::new())), + closed: Arc::new(AtomicBool::new(false)), + recv_time: Arc::new(AtomicU64::new(0)), + internal_recv: Arc::new(None), + } + } + + /// Connects to a RakNet server. + /// > Note: This is an async function. You must await it. + pub async fn connect Into>>( + &mut self, + addr: Addr, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + return Err(ClientError::AlreadyOnline); + } + + let addrr: PossiblySocketAddr = addr.into(); + let address: SocketAddr = match addrr.to_socket_addr() { + Some(a) => a, + None => { + rakrs_debug!("Invalid address provided"); + return Err(ClientError::AddrBindErr); + } + }; + + let sock = match UdpSocket::bind(address).await { + Ok(s) => s, + Err(e) => { + rakrs_debug!("Failed to bind to address: {}", e); + return Err(ClientError::Killed); + } + }; + + let socket = Arc::new(sock); + let send_queue = Arc::new(RwLock::new(SendQueue::new( + self.mtu, + 12000, + 5, + socket.clone(), + address, + ))); + + self.send_queue = Some(send_queue.clone()); + let (net_send, net_recv) = bounded::>(10); + + self.network_recv = Arc::new(Some(Mutex::new(net_recv))); + + let closer = self.closed.clone(); + + let socket_task = task::spawn(async move { + let mut buf: [u8; 2048] = [0; 2048]; + + loop { + + if closer.load(std::sync::atomic::Ordering::Relaxed) { + rakrs_debug!(true, "[CLIENT] Network recv task closed"); + break; + } + + let length: usize; + + let recv = socket.recv(&mut buf).await; + match recv { + Ok(l) => length = l, + Err(e) => { + rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); + continue; + } + } + + // no assertions because this is a client + // this allows the user to customize their own packet handling + if let Err(_) = net_send.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); + } + } + }); + + let recv_task = self.init_recv_task().await?; + let tick_task = self.init_connect_tick(send_queue.clone()).await?; + + self.push_task(socket_task).await; + self.push_task(recv_task).await; + self.push_task(tick_task).await; + + Ok(()) + } + + /// Updates the client state + pub async fn update_state(&mut self, new_state: ConnectionState) { + let mut state = self.state.lock().await; + *state = new_state; + } + + async fn push_task(&mut self, task: JoinHandle<()>) { + self.tasks.lock().await.push(task); + } + + async fn init_recv_task(&mut self) -> Result, ClientError> { + + } + + /// This is an internal function that initializes the client connection. + /// This is called by `Client::connect()`. + async fn init_connect_tick(&mut self, send_queue: Arc>) -> Result, ClientError> { + // verify that the client is offline + if self.state.lock().await.is_available() { + return Err(ClientError::AlreadyOnline); + } + + let closer = self.closed.clone(); + let recv_queue = self.recv_queue.clone(); + let state = self.state.clone(); + let last_recv = self.recv_time.clone(); + let mut last_ping: u16 = 0; + + let t = task::spawn(async move { + loop { + sleep(Duration::from_millis(50)).await; + + if closer.load(std::sync::atomic::Ordering::Relaxed) { + rakrs_debug!(true, "[CLIENT] Connect tick task closed"); + break; + } + + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut state = state.lock().await; + + if *state == ConnectionState::Disconnected { + rakrs_debug!(true, "[CLIENT] Client is disconnected. Closing connect tick task"); + closer.store(true, std::sync::atomic::Ordering::Relaxed); + break; + } + + if recv + 20000 <= current_epoch() { + *state = ConnectionState::Disconnected; + rakrs_debug!(true, "[CLIENT] Client timed out. Closing connection..."); + closer.store(true, std::sync::atomic::Ordering::Relaxed); + break; + } + + if recv + 15000 <= current_epoch() && state.is_reliable() { + *state = ConnectionState::TimingOut; + rakrs_debug!( + true, + "[CLIENT] Connection is timing out, sending a ping!", + ); + } + + let mut send_q = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; + + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = send_q + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {} + last_ping = 0; + } else { + last_ping += 50; + } + + send_q.update().await; + + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.parse() { + send_q.send_stream(&p).await; + } + } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.parse() { + send_q.send_stream(&p).await; + } + } + } + }); + Ok(t) + } +} diff --git a/src/error/client.rs b/src/error/client.rs new file mode 100644 index 0000000..e808c1d --- /dev/null +++ b/src/error/client.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ClientError { + AddrBindErr, + AlreadyOnline, + NotListening, + Killed, + Reset, +} \ No newline at end of file diff --git a/src/error/connection.rs b/src/error/connection.rs index 5543865..d2a54ab 100644 --- a/src/error/connection.rs +++ b/src/error/connection.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ConnectionError { Closed, EventDispatchError, diff --git a/src/error/mod.rs b/src/error/mod.rs index f63bcdd..c2ee5b7 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,2 +1,3 @@ +pub mod client; pub mod connection; pub mod server; diff --git a/src/error/server.rs b/src/error/server.rs index 636d761..b54929d 100644 --- a/src/error/server.rs +++ b/src/error/server.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ServerError { AddrBindErr, AlreadyOnline, diff --git a/src/lib.rs b/src/lib.rs index ef67fc7..8cc5bc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod client; pub mod connection; pub mod error; pub mod protocol; diff --git a/src/server/mod.rs b/src/server/mod.rs index 58667be..a189ac8 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -81,6 +81,17 @@ impl From for PossiblySocketAddr<'_> { } } +impl std::fmt::Display for PossiblySocketAddr<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PossiblySocketAddr::SocketAddr(addr) => write!(f, "{}", addr), + PossiblySocketAddr::Str(addr) => write!(f, "{}", addr), + PossiblySocketAddr::String(addr) => write!(f, "{}", addr), + PossiblySocketAddr::ActuallyNot => write!(f, "Not a valid address!"), + } + } +} + pub struct Listener { /// If mcpe is true, this is the default MOTD, this is /// the default MOTD to send to the client. You can change this later by setting From cc3f3b3aeb7019e730c0b07d9b66473f413f5d51 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sat, 10 Jun 2023 22:09:55 -0500 Subject: [PATCH 38/75] fmt(src/client/mod.rs): Format command [ci skip] --- src/client/mod.rs | 45 +++++++++++++++++++++++++++++++-------------- src/error/client.rs | 2 +- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 8eaf4ad..00ea7d9 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,4 +1,11 @@ -use std::{sync::{Arc, atomic::{AtomicBool, AtomicU64}}, net::SocketAddr, time::Duration}; +use std::{ + net::SocketAddr, + sync::{ + atomic::{AtomicBool, AtomicU64}, + Arc, + }, + time::Duration, +}; #[cfg(feature = "async_std")] use async_std::{ @@ -20,8 +27,18 @@ use tokio::{ }; use crate::{ - connection::{queue::{RecvQueue, SendQueue, send}, state::ConnectionState}, - server::{PossiblySocketAddr, current_epoch}, error::client::ClientError, rakrs_debug, protocol::{packet::{Packet, online::ConnectedPing}, reliability::Reliability, ack::Ack}, + connection::{ + queue::{send, RecvQueue, SendQueue}, + state::ConnectionState, + }, + error::client::ClientError, + protocol::{ + ack::Ack, + packet::{online::ConnectedPing, Packet}, + reliability::Reliability, + }, + rakrs_debug, + server::{current_epoch, PossiblySocketAddr}, }; /// This struct is used to connect to RakNet servers. @@ -105,7 +122,7 @@ impl Client { ))); self.send_queue = Some(send_queue.clone()); - let (net_send, net_recv) = bounded::>(10); + let (net_send, net_recv) = bounded::>(10); self.network_recv = Arc::new(Some(Mutex::new(net_recv))); @@ -115,7 +132,6 @@ impl Client { let mut buf: [u8; 2048] = [0; 2048]; loop { - if closer.load(std::sync::atomic::Ordering::Relaxed) { rakrs_debug!(true, "[CLIENT] Network recv task closed"); break; @@ -160,13 +176,14 @@ impl Client { self.tasks.lock().await.push(task); } - async fn init_recv_task(&mut self) -> Result, ClientError> { - - } + async fn init_recv_task(&mut self) -> Result, ClientError> {} /// This is an internal function that initializes the client connection. /// This is called by `Client::connect()`. - async fn init_connect_tick(&mut self, send_queue: Arc>) -> Result, ClientError> { + async fn init_connect_tick( + &mut self, + send_queue: Arc>, + ) -> Result, ClientError> { // verify that the client is offline if self.state.lock().await.is_available() { return Err(ClientError::AlreadyOnline); @@ -191,7 +208,10 @@ impl Client { let mut state = state.lock().await; if *state == ConnectionState::Disconnected { - rakrs_debug!(true, "[CLIENT] Client is disconnected. Closing connect tick task"); + rakrs_debug!( + true, + "[CLIENT] Client is disconnected. Closing connect tick task" + ); closer.store(true, std::sync::atomic::Ordering::Relaxed); break; } @@ -205,10 +225,7 @@ impl Client { if recv + 15000 <= current_epoch() && state.is_reliable() { *state = ConnectionState::TimingOut; - rakrs_debug!( - true, - "[CLIENT] Connection is timing out, sending a ping!", - ); + rakrs_debug!(true, "[CLIENT] Connection is timing out, sending a ping!",); } let mut send_q = send_queue.write().await; diff --git a/src/error/client.rs b/src/error/client.rs index e808c1d..9c77c9c 100644 --- a/src/error/client.rs +++ b/src/error/client.rs @@ -5,4 +5,4 @@ pub enum ClientError { NotListening, Killed, Reset, -} \ No newline at end of file +} From 29e9de408d33548eb1a9754cc201d49d2d6e0f71 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 17:23:13 -0500 Subject: [PATCH 39/75] feat: Handshake --- src/client/handshake.rs | 339 ++++++++++++++++++++++++++++++++++++++++ src/client/mod.rs | 82 ++++++++-- src/error/client.rs | 1 + 3 files changed, 413 insertions(+), 9 deletions(-) create mode 100644 src/client/handshake.rs diff --git a/src/client/handshake.rs b/src/client/handshake.rs new file mode 100644 index 0000000..6729167 --- /dev/null +++ b/src/client/handshake.rs @@ -0,0 +1,339 @@ +use std::sync::Arc; +use std::sync::Mutex; + +#[cfg(feature = "async_std")] +use async_std::{ + future::Future, + net::UdpSocket, + task::Poll, + task::Waker, + task::{self}, +}; + +use binary_utils::Streamable; +#[cfg(feature = "async_tokio")] +use tokio::{ + future::Future, + net::UdpSocket, + task::Poll, + task::Waker, + task::{self}, +}; + +use crate::connection::queue::send::SendQueue; +use crate::connection::queue::RecvQueue; +use crate::protocol::frame::FramePacket; +use crate::protocol::packet::offline::{ + IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, + SessionInfoRequest, +}; +use crate::protocol::packet::online::{ConnectionRequest, NewConnection, OnlinePacket}; +use crate::protocol::packet::Packet; +use crate::protocol::packet::PacketId; +use crate::protocol::reliability::Reliability; +use crate::protocol::Magic; +use crate::rakrs_debug; +use crate::server::current_epoch; + +macro_rules! match_ids { + ($socket: expr, $($ids: expr),*) => { + { + let mut recv_buf: [u8; 2048] = [0; 2048]; + let mut tries: u8 = 0; + let ids = vec![$($ids),*]; + let mut pk: Option> = None; + + loop { + if (tries >= 5) { + break; + } + + let len: usize; + let rc = $socket.recv(&mut recv_buf).await; + + match rc { + Err(_) => { + tries += 1; + continue; + }, + Ok(l) => len = l + }; + + if ids.contains(&recv_buf[0]) { + pk = Some(recv_buf[..len].to_vec()); + break; + } + } + + pk + } + }; +} + +macro_rules! expect_reply { + ($socket: expr, $reply: ty) => {{ + let mut recv_buf: [u8; 2048] = [0; 2048]; + let mut tries: u8 = 0; + let mut pk: Option<$reply> = None; + + loop { + if (tries >= 5) { + break; + } + + let len: usize; + let rc = $socket.recv(&mut recv_buf).await; + + match rc { + Err(_) => { + tries += 1; + continue; + } + Ok(l) => len = l, + }; + + if let Ok(packet) = <$reply>::compose(&mut recv_buf[1..len], &mut 0) { + pk = Some(packet); + break; + } + } + + pk + }}; +} + +macro_rules! update_state { + ($done: expr, $shared_state: expr, $state: expr) => {{ + let mut state = $shared_state.lock().unwrap(); + state.status = $state; + state.done = true; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + return; + }}; + ($shared_state: expr, $state: expr) => {{ + let mut state = $shared_state.lock().unwrap(); + state.status = $state; + state.done = false; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + }}; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum HandshakeStatus { + Created, + Opening, + SessionOpen, + Failed, + IncompatibleVersion, + Completed, +} + +struct HandshakeState { + status: HandshakeStatus, + done: bool, + waker: Option, +} + +pub struct ClientHandshake { + status: Arc>, +} + +impl ClientHandshake { + pub fn new(socket: Arc, id: i64, version: u8, mtu: u16, attempts: u8) -> Self { + let state = Arc::new(Mutex::new(HandshakeState { + done: false, + status: HandshakeStatus::Created, + waker: None, + })); + + let shared_state = state.clone(); + + task::spawn(async move { + let connect_request = OpenConnectRequest { + magic: Magic::new(), + protocol: version, + mtu_size: mtu, + }; + + update_state!(shared_state, HandshakeStatus::Opening); + + send_packet(&socket, connect_request.into()).await; + let reply = match_ids!( + socket.clone(), + OpenConnectReply::id(), + IncompatibleProtocolVersion::id() + ); + + if reply.is_none() { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + if let Ok(_) = + IncompatibleProtocolVersion::compose(&mut reply.clone().unwrap()[1..], &mut 0) + { + update_state!(true, shared_state, HandshakeStatus::IncompatibleVersion); + } + + let open_reply = OpenConnectReply::compose(&mut reply.unwrap()[1..], &mut 0); + + if open_reply.is_err() { + let mut state = shared_state.lock().unwrap(); + state.status = HandshakeStatus::Failed; + state.done = true; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + return; + } + + let session_info = SessionInfoRequest { + magic: Magic::new(), + address: socket.peer_addr().unwrap(), + mtu_size: mtu, + client_id: id, + }; + + update_state!(shared_state, HandshakeStatus::SessionOpen); + + send_packet(&socket, session_info.into()).await; + + let session_reply = expect_reply!(socket, SessionInfoReply); + + if session_reply.is_none() { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + let session_reply = session_reply.unwrap(); + + if session_reply.mtu_size != mtu { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + // create a temporary sendq + let mut send_q = SendQueue::new( + mtu, + 5000, + attempts.clone().into(), + socket.clone(), + socket.peer_addr().unwrap(), + ); + let mut recv_q = RecvQueue::new(); + + let connect_request = ConnectionRequest { + time: current_epoch() as i64, + client_id: id, + }; + + if let Err(_) = send_q + .insert( + Packet::from(connect_request).parse().unwrap(), + Reliability::ReliableOrd, + true, + None, + ) + .await + { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + let mut buf: [u8; 2048] = [0; 2048]; + + loop { + let len: usize; + let rec = socket.recv_from(&mut buf).await; + + match rec { + Err(_) => { + continue; + } + Ok((l, _)) => len = l, + }; + + // proccess frame packet + match buf[0] { + 0x80..=0x8d => { + if let Ok(pk) = FramePacket::compose(&mut buf[..len], &mut 0) { + recv_q.insert(pk).unwrap(); + + let raw_packets = recv_q.flush(); + + for mut raw_pk in raw_packets { + let pk = Packet::compose(&mut raw_pk[..], &mut 0); + + if let Ok(pk) = pk { + if pk.is_online() { + match pk.get_online() { + OnlinePacket::ConnectionAccept(pk) => { + // send new incoming connection + let new_incoming = NewConnection { + server_address: socket.peer_addr().unwrap(), + system_address: socket.local_addr().unwrap(), + request_time: pk.request_time, + timestamp: pk.timestamp, + }; + if let Err(_) = send_q + .insert( + Packet::from(new_incoming).parse().unwrap(), + Reliability::ReliableOrd, + true, + None, + ) + .await + { + update_state!( + true, + shared_state, + HandshakeStatus::Failed + ); + } else { + update_state!( + true, + shared_state, + HandshakeStatus::Completed + ); + } + } + _ => {} + } + } + } + } + } + } + _ => {} + } + } + }); + + Self { status: state } + } +} + +impl Future for ClientHandshake { + type Output = HandshakeStatus; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll { + // see if we can finish + let mut state = self.status.lock().unwrap(); + + if state.done { + return Poll::Ready(state.status); + } else { + state.waker = Some(cx.waker().clone()); + return Poll::Pending; + } + } +} + +async fn send_packet(socket: &Arc, packet: Packet) { + if let Err(e) = socket.send(&mut packet.parse().unwrap()[..]).await { + rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 00ea7d9..17612cc 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,3 +1,5 @@ +pub mod handshake; + use std::{ net::SocketAddr, sync::{ @@ -34,13 +36,17 @@ use crate::{ error::client::ClientError, protocol::{ ack::Ack, - packet::{online::ConnectedPing, Packet}, + packet::{offline::OfflinePacket, online::ConnectedPing, Packet, Payload}, reliability::Reliability, }, rakrs_debug, server::{current_epoch, PossiblySocketAddr}, }; +pub const DEFAULT_MTU: u16 = 1400; + +use self::handshake::{ClientHandshake, HandshakeStatus}; + /// This struct is used to connect to RakNet servers. /// To start a connection, use `Client::connect()`. pub struct Client { @@ -52,10 +58,11 @@ pub struct Client { /// This is read from before sending recv_queue: Arc>, /// The network recieve channel is used to receive raw packets from the server. - network_recv: Arc>>>>, - /// The internal channel that is used to process packets - internal_recv: Arc>>>>, - internal_send: Arc>>>, + network_recv: Option>>>>, + /// The internal channel that is used to dispatch packets to a higher level. + internal_recv: Receiver>, + internal_send: Sender>, + /// A list of tasks that are killed when the connection drops. tasks: Arc>>>, /// A notifier for when the client should kill threads. closed: Arc, @@ -65,23 +72,28 @@ pub struct Client { mtu: u16, /// The RakNet version of the client. version: u8, + /// The internal client id of the client. + id: u64, } impl Client { /// Creates a new client. /// > Note: This does not start a connection. You must use `Client::connect()` to start a connection. pub fn new(version: u8, mtu: u16) -> Self { + let (internal_send, internal_recv) = bounded::>(10); Self { state: Arc::new(Mutex::new(ConnectionState::Offline)), send_queue: None, recv_queue: Arc::new(Mutex::new(RecvQueue::new())), - network_recv: Arc::new(None), + network_recv: None, mtu, version, tasks: Arc::new(Mutex::new(Vec::new())), closed: Arc::new(AtomicBool::new(false)), recv_time: Arc::new(AtomicU64::new(0)), - internal_recv: Arc::new(None), + internal_recv, + internal_send, + id: rand::random::(), } } @@ -124,10 +136,19 @@ impl Client { self.send_queue = Some(send_queue.clone()); let (net_send, net_recv) = bounded::>(10); - self.network_recv = Arc::new(Some(Mutex::new(net_recv))); + self.network_recv = Some(Arc::new(Mutex::new(net_recv))); let closer = self.closed.clone(); + // before we even start the connection, we need to complete the handshake + let handshake = + ClientHandshake::new(socket.clone(), self.id as i64, self.version, self.mtu, 5).await; + + if handshake != HandshakeStatus::Completed { + rakrs_debug!("Failed to complete handshake: {:?}", handshake); + return Err(ClientError::Killed); + } + let socket_task = task::spawn(async move { let mut buf: [u8; 2048] = [0; 2048]; @@ -159,8 +180,11 @@ impl Client { let recv_task = self.init_recv_task().await?; let tick_task = self.init_connect_tick(send_queue.clone()).await?; + // Responsible for the raw socket self.push_task(socket_task).await; + // Responsible for digesting messages from the network self.push_task(recv_task).await; + // Responsible for sending packets to the server and keeping the connection alive self.push_task(tick_task).await; Ok(()) @@ -176,7 +200,39 @@ impl Client { self.tasks.lock().await.push(task); } - async fn init_recv_task(&mut self) -> Result, ClientError> {} + async fn init_recv_task(&mut self) -> Result, ClientError> { + let net_recv = match self.network_recv { + Some(ref n) => n.clone(), + None => { + rakrs_debug!("[CLIENT] Network recv channel is not initialized"); + return Err(ClientError::Killed); + } + }; + + let recv_queue = self.recv_queue.clone(); + let internal_sender = self.internal_send.clone(); + let closed = self.closed.clone(); + let state = self.state.clone(); + let version = self.version; + + let r = task::spawn(async move { + loop { + let pk_recv = net_recv.lock().await.recv().await; + if closed.load(std::sync::atomic::Ordering::Relaxed) == true { + rakrs_debug!("[CLIENT] The internal network recieve channel has been killed."); + break; + } + + if let Err(_) = pk_recv { + rakrs_debug!(true, "[CLIENT] Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + let buffer = pk_recv.unwrap(); + } + }); + Ok(r) + } /// This is an internal function that initializes the client connection. /// This is called by `Client::connect()`. @@ -216,6 +272,14 @@ impl Client { break; } + if *state == ConnectionState::Connecting { + rakrs_debug!( + true, + "[CLIENT] Client is not fully connected to the server yet." + ); + continue; + } + if recv + 20000 <= current_epoch() { *state = ConnectionState::Disconnected; rakrs_debug!(true, "[CLIENT] Client timed out. Closing connection..."); diff --git a/src/error/client.rs b/src/error/client.rs index 9c77c9c..a11b552 100644 --- a/src/error/client.rs +++ b/src/error/client.rs @@ -3,6 +3,7 @@ pub enum ClientError { AddrBindErr, AlreadyOnline, NotListening, + IncompatibleProtocolVersion, Killed, Reset, } From bc166d967a4b17983bb07f4bc93b5c16f6c55526 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 19:43:32 -0500 Subject: [PATCH 40/75] feat: client base completed --- src/client/mod.rs | 309 +++++++++++++++++++++++++++- src/connection/controller/window.rs | 2 +- src/connection/mod.rs | 2 +- src/connection/queue/mod.rs | 2 +- src/connection/queue/send.rs | 2 +- src/error/client.rs | 4 + src/server/mod.rs | 7 +- 7 files changed, 313 insertions(+), 15 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 17612cc..efbcb54 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -9,6 +9,8 @@ use std::{ time::Duration, }; +#[cfg(feature = "async_tokio")] +use crate::connection::RecvError; #[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, RecvError, Sender}, @@ -30,13 +32,17 @@ use tokio::{ use crate::{ connection::{ - queue::{send, RecvQueue, SendQueue}, + queue::{RecvQueue, SendQueue}, state::ConnectionState, }, error::client::ClientError, protocol::{ - ack::Ack, - packet::{offline::OfflinePacket, online::ConnectedPing, Packet, Payload}, + ack::{Ack, Ackable, ACK, NACK}, + frame::FramePacket, + packet::{ + online::{ConnectedPing, ConnectedPong, OnlinePacket}, + Packet, + }, reliability::Reliability, }, rakrs_debug, @@ -171,6 +177,7 @@ impl Client { // no assertions because this is a client // this allows the user to customize their own packet handling + // todo: the logic in the recv_task may be better here, as this is latent if let Err(_) = net_send.send(buf[..length].to_vec()).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); } @@ -196,6 +203,134 @@ impl Client { *state = new_state; } + /// Todo: send disconnect packet. + pub async fn close(&mut self) { + self.update_state(ConnectionState::Disconnecting).await; + self.closed + .store(true, std::sync::atomic::Ordering::Relaxed); + let mut tasks = self.tasks.lock().await; + for task in tasks.drain(..) { + task.cancel().await; + } + } + + pub async fn send_ord(&mut self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert( + buffer.to_vec(), + Reliability::ReliableOrd, + false, + Some(channel), + ) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::NotListening) + } + } + + pub async fn send_seq(&mut self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert( + buffer.to_vec(), + Reliability::ReliableSeq, + false, + Some(channel), + ) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn send( + &mut self, + buffer: &[u8], + reliability: Reliability, + channel: u8, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer.to_vec(), reliability, false, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn send_immediate( + &mut self, + buffer: &[u8], + reliability: Reliability, + channel: u8, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer.to_vec(), reliability, true, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn flush_ack(&mut self) { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + let mut recv_q = self.recv_queue.lock().await; + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.parse() { + send_q.send_stream(&p).await; + } + } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.parse() { + send_q.send_stream(&p).await; + } + } + } + + pub async fn recv(&mut self) -> Result, RecvError> { + match self.internal_recv.recv().await { + #[cfg(feature = "async_std")] + Ok(packet) => Ok(packet), + #[cfg(feature = "async_std")] + Err(e) => Err(e), + #[cfg(feature = "async_tokio")] + Some(packet) => Ok(packet), + #[cfg(feature = "async_tokio")] + None => Err(RecvError::Closed), + } + } + async fn push_task(&mut self, task: JoinHandle<()>) { self.tasks.lock().await.push(task); } @@ -204,7 +339,15 @@ impl Client { let net_recv = match self.network_recv { Some(ref n) => n.clone(), None => { - rakrs_debug!("[CLIENT] Network recv channel is not initialized"); + rakrs_debug!("[CLIENT] (recv_task) Network recv channel is not initialized"); + return Err(ClientError::Killed); + } + }; + + let send_queue = match self.send_queue { + Some(ref s) => s.clone(), + None => { + rakrs_debug!("[CLIENT] (recv_task) Send queue is not initialized"); return Err(ClientError::Killed); } }; @@ -213,22 +356,165 @@ impl Client { let internal_sender = self.internal_send.clone(); let closed = self.closed.clone(); let state = self.state.clone(); - let version = self.version; + let recv_time = self.recv_time.clone(); let r = task::spawn(async move { loop { let pk_recv = net_recv.lock().await.recv().await; if closed.load(std::sync::atomic::Ordering::Relaxed) == true { - rakrs_debug!("[CLIENT] The internal network recieve channel has been killed."); + rakrs_debug!("[CLIENT] (recv_task) The internal network recieve channel has been killed."); break; } + #[cfg(feature = "async_std")] if let Err(_) = pk_recv { - rakrs_debug!(true, "[CLIENT] Failed to recieve anything on netowrk channel, is there a sender?"); + rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + #[cfg(feature = "async_tokio")] + if let None = pk_recv { + rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); continue; } - let buffer = pk_recv.unwrap(); + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + + let mut client_state = state.lock().await; + + if *client_state == ConnectionState::TimingOut { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is no longer timing out!"); + *client_state = ConnectionState::Connected; + } + + if *client_state == ConnectionState::Disconnecting { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is disconnecting!"); + break; + } + + // drop here so the lock isn't held for too long + drop(client_state); + + let mut buffer = pk_recv.unwrap(); + + match buffer[0] { + 0x80..=0x8d => { + if let Ok(frame_packet) = FramePacket::compose(&mut buffer[..], &mut 0) { + let mut recv_q = recv_queue.lock().await; + if let Err(_) = recv_q.insert(frame_packet) { + rakrs_debug!( + true, + "[CLIENT] Failed to push frame packet into send queue." + ); + } + + let buffers = recv_q.flush(); + + for mut pk_buf in buffers { + if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { + if packet.is_online() { + match packet.get_online() { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_queue.write().await; + if let Err(_) = q + .send_packet( + response.into(), + Reliability::Reliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + } + OnlinePacket::ConnectedPong(_) => { + // todo: add ping time to client + rakrs_debug!( + true, + "[CLIENT] Recieved pong packet!" + ); + } + _ => {} + }; + + #[cfg(feature = "async_std")] + if let Err(_) = internal_sender.send(pk_buf).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + #[cfg(feature = "async_tokio")] + if let Some(_) = internal_sender.send(pk_buf).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } else { + // we should never recieve an offline packet after the handshake + rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); + } + } else { + rakrs_debug!( + true, + "[CLIENT] Failed to send packet to client (parsing failed)!", + ); + } + } + } + } + NACK => { + if let Ok(nack) = Ack::compose(&mut buffer[..], &mut 0) { + let mut send_q = send_queue.write().await; + let to_resend = send_q.nack(nack); + + if to_resend.len() > 0 { + for ack_packet in to_resend { + if let Ok(buffer) = ack_packet.parse() { + if let Err(_) = send_q + .insert(buffer, Reliability::Unreliable, true, Some(0)) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to insert ack packet into send queue!" + ); + } + } else { + rakrs_debug!( + true, + "[CLIENT] Failed to send packet to client (parsing failed)!", + ); + } + } + } + } + } + ACK => { + if let Ok(ack) = Ack::compose(&mut buffer[..], &mut 0) { + let mut send_q = send_queue.write().await; + send_q.ack(ack.clone()); + + drop(send_q); + + recv_queue.lock().await.ack(ack); + } + } + _ => { + // we don't know what this is, so we're going to send it to the user, maybe + // this is a custom packet + #[cfg(feature = "async_std")] + if let Err(_) = internal_sender.send(buffer).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + #[cfg(feature = "async_tokio")] + if let Some(_) = internal_sender.send(buffer).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } } }); Ok(r) @@ -330,3 +616,10 @@ impl Client { Ok(t) } } + +impl Drop for Client { + fn drop(&mut self) { + self.closed + .store(true, std::sync::atomic::Ordering::Relaxed); + } +} diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs index 29300e2..5a3b8fc 100644 --- a/src/connection/controller/window.rs +++ b/src/connection/controller/window.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{rakrs_debug, server::current_epoch}; +use crate::{server::current_epoch}; #[derive(Debug, Clone)] pub struct ReliableWindow { diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 75cdabf..2111e92 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -239,7 +239,7 @@ impl Connection { /// pub async fn init_net_recv( &self, - mut net: Receiver>, + net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index 390bccb..a399279 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -399,7 +399,7 @@ impl FragmentQueue { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum FragmentQueueError { FrameExists, FrameNotFragmented, diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index f72f30d..5d689f4 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -18,7 +18,7 @@ use crate::util::{to_address_token, SafeGenerator}; use super::{FragmentQueue, FragmentQueueError, NetQueue, RecoveryQueue}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum SendQueueError { /// The packet is too large to be sent. PacketTooLarge, diff --git a/src/error/client.rs b/src/error/client.rs index a11b552..fbc04f1 100644 --- a/src/error/client.rs +++ b/src/error/client.rs @@ -1,9 +1,13 @@ +use crate::connection::queue::SendQueueError; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ClientError { AddrBindErr, AlreadyOnline, NotListening, + Unavailable, IncompatibleProtocolVersion, Killed, Reset, + SendQueueError(SendQueueError), } diff --git a/src/server/mod.rs b/src/server/mod.rs index a189ac8..27e6937 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -193,6 +193,7 @@ impl Listener { let send_comm = self.send_comm.clone(); // let send_evt = self.send_evnt.clone(); let server_id = self.id.clone(); + #[cfg(feature = "mcpe")] let default_motd = self.motd.clone(); let connections = self.connections.clone(); let closer = self.closed.clone(); @@ -204,6 +205,7 @@ impl Listener { task::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; + #[cfg(feature = "mcpe")] let motd_default = default_motd.clone(); loop { @@ -372,9 +374,8 @@ impl Listener { // update the sessions mtuSize, this is referred to internally, we also will send this event to the client // event channel. However we are not expecting a response. - drop( - sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size, - ); + + sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; // let (resp_tx, resp_rx) = oneshot::channel::(); From 3fe357fc7b4ce47a6da198cf1bf51e4be3e27e3c Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 19:44:25 -0500 Subject: [PATCH 41/75] fmt: Run cargo fmt --- .gitignore | 2 +- src/connection/controller/window.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bf60a69..e157ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ Cargo.lock *.old/ *.profraw -raknet-test/ \ No newline at end of file +raknet-test/ diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs index 5a3b8fc..ac5da81 100644 --- a/src/connection/controller/window.rs +++ b/src/connection/controller/window.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{server::current_epoch}; +use crate::server::current_epoch; #[derive(Debug, Clone)] pub struct ReliableWindow { From de075a474e8fb145795393327e000f6ef0c73ff2 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 20:37:08 -0500 Subject: [PATCH 42/75] chore: Fix tokio --- .gitignore | 4 +--- Cargo.toml | 4 ++-- examples/README.md | 5 +++++ examples/async-std/client/Cargo.toml | 11 ++++++++++ examples/async-std/client/src/main.rs | 13 ++++++++++++ examples/async-std/server/Cargo.toml | 8 ++++++++ examples/async-std/server/src/main.rs | 3 +++ src/client/handshake.rs | 26 +++++++++++++++--------- src/client/mod.rs | 29 ++++++++++++++++----------- src/connection/mod.rs | 2 +- 10 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 examples/README.md create mode 100644 examples/async-std/client/Cargo.toml create mode 100644 examples/async-std/client/src/main.rs create mode 100644 examples/async-std/server/Cargo.toml create mode 100644 examples/async-std/server/src/main.rs diff --git a/.gitignore b/.gitignore index e157ac5..c5ff06a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,4 @@ target Cargo.lock *.old/ -*.profraw - -raknet-test/ +*.profraw \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 8c43e99..e8713aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -tokio = { version = "1.15.0", features = ["full"], optional = true } +tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" futures-executor = "0.3.19" -async-std = { version = "1.10.0", optional = true, features = [ "unstable" ] } +async-std = { version = "1.12.0", optional = true, features = [ "unstable" ] } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..52c89c8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Examples + +**async-std:** +- [client](/examples/async-std/client) +- [server](/examples/async-std/server) \ No newline at end of file diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml new file mode 100644 index 0000000..d37c621 --- /dev/null +++ b/examples/async-std/client/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } +binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } +rakrs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async-std" ]} \ No newline at end of file diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs new file mode 100644 index 0000000..dae80c9 --- /dev/null +++ b/examples/async-std/client/src/main.rs @@ -0,0 +1,13 @@ +use rakrs::client::{Client, DEFAULT_MTU}; + +#[async_std::main] +async fn main() { + let mut client = Client::new(8, DEFAULT_MTU); + + client.connect("127.0.0.1:19132").await.unwrap(); + + loop { + let pk = client.recv().await.unwrap(); + println!("Received packet: {:?}", pk); + } +} \ No newline at end of file diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml new file mode 100644 index 0000000..dd022ab --- /dev/null +++ b/examples/async-std/server/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/examples/async-std/server/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 6729167..133b0b4 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -12,11 +12,12 @@ use async_std::{ use binary_utils::Streamable; #[cfg(feature = "async_tokio")] +use std::future::Future; +#[cfg(feature = "async_tokio")] +use std::task::{Context, Poll, Waker}; +#[cfg(feature = "async_tokio")] use tokio::{ - future::Future, net::UdpSocket, - task::Poll, - task::Waker, task::{self}, }; @@ -161,7 +162,10 @@ impl ClientHandshake { update_state!(shared_state, HandshakeStatus::Opening); - send_packet(&socket, connect_request.into()).await; + if !send_packet(&socket, connect_request.into()).await { + update_state!(true, shared_state, HandshakeStatus::Failed); + }; + let reply = match_ids!( socket.clone(), OpenConnectReply::id(), @@ -199,7 +203,9 @@ impl ClientHandshake { update_state!(shared_state, HandshakeStatus::SessionOpen); - send_packet(&socket, session_info.into()).await; + if !send_packet(&socket, session_info.into()).await { + update_state!(true, shared_state, HandshakeStatus::Failed); + } let session_reply = expect_reply!(socket, SessionInfoReply); @@ -316,10 +322,7 @@ impl ClientHandshake { impl Future for ClientHandshake { type Output = HandshakeStatus; - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut task::Context<'_>, - ) -> task::Poll { + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // see if we can finish let mut state = self.status.lock().unwrap(); @@ -332,8 +335,11 @@ impl Future for ClientHandshake { } } -async fn send_packet(socket: &Arc, packet: Packet) { +async fn send_packet(socket: &Arc, packet: Packet) -> bool { if let Err(e) = socket.send(&mut packet.parse().unwrap()[..]).await { rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); + return false; + } else { + return true; } } diff --git a/src/client/mod.rs b/src/client/mod.rs index efbcb54..9d9ffeb 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -12,6 +12,8 @@ use std::{ #[cfg(feature = "async_tokio")] use crate::connection::RecvError; #[cfg(feature = "async_std")] +use async_std::future::timeout; +#[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, RecvError, Sender}, net::UdpSocket, @@ -19,6 +21,7 @@ use async_std::{ task::{self, sleep, JoinHandle}, }; use binary_utils::Streamable; + #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -27,7 +30,7 @@ use tokio::{ Mutex, RwLock, }, task::{self, JoinHandle}, - time::sleep, + time::{sleep, timeout}, }; use crate::{ @@ -122,7 +125,7 @@ impl Client { } }; - let sock = match UdpSocket::bind(address).await { + let sock = match UdpSocket::bind("0.0.0.0:0").await { Ok(s) => s, Err(e) => { rakrs_debug!("Failed to bind to address: {}", e); @@ -130,6 +133,15 @@ impl Client { } }; + if timeout(Duration::from_secs(10), sock.connect(address)) + .await + .is_err() + { + rakrs_debug!("[CLIENT] Failed to connect to address"); + self.close().await; + return Err(ClientError::Killed); + } + let socket = Arc::new(sock); let send_queue = Arc::new(RwLock::new(SendQueue::new( self.mtu, @@ -210,7 +222,10 @@ impl Client { .store(true, std::sync::atomic::Ordering::Relaxed); let mut tasks = self.tasks.lock().await; for task in tasks.drain(..) { + #[cfg(feature = "async_std")] task.cancel().await; + #[cfg(feature = "async_tokio")] + task.abort(); } } @@ -444,14 +459,9 @@ impl Client { _ => {} }; - #[cfg(feature = "async_std")] if let Err(_) = internal_sender.send(pk_buf).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } - #[cfg(feature = "async_tokio")] - if let Some(_) = internal_sender.send(pk_buf).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); - } } else { // we should never recieve an offline packet after the handshake rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); @@ -505,14 +515,9 @@ impl Client { _ => { // we don't know what this is, so we're going to send it to the user, maybe // this is a custom packet - #[cfg(feature = "async_std")] if let Err(_) = internal_sender.send(buffer).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } - #[cfg(feature = "async_tokio")] - if let Some(_) = internal_sender.send(buffer).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); - } } } } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 2111e92..75cdabf 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -239,7 +239,7 @@ impl Connection { /// pub async fn init_net_recv( &self, - net: Receiver>, + mut net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); From 75c13fe3bf295cf1462c6ce64d0d582b89cc5b8e Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 20:38:46 -0500 Subject: [PATCH 43/75] chore: Fix async std --- src/client/handshake.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 133b0b4..90f877c 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -5,9 +5,12 @@ use std::sync::Mutex; use async_std::{ future::Future, net::UdpSocket, - task::Poll, - task::Waker, - task::{self}, + task::{ + Context, + Poll, + Waker, + self + }, }; use binary_utils::Streamable; From 8492dce37b117aad7619f9dc2462b0572d92cb80 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 22:19:28 -0500 Subject: [PATCH 44/75] feat: client fixes --- examples/async-std/client/Cargo.toml | 2 +- examples/async-std/client/src/main.rs | 4 +- src/client/handshake.rs | 60 ++++++++++++++++++++--- src/client/mod.rs | 70 ++++++++++++++++++++++++--- src/error/client.rs | 1 + src/protocol/packet/online.rs | 1 + 6 files changed, 119 insertions(+), 19 deletions(-) diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml index d37c621..38913b2 100644 --- a/examples/async-std/client/Cargo.toml +++ b/examples/async-std/client/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -rakrs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async-std" ]} \ No newline at end of file +rakrs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index dae80c9..fce75d9 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -2,9 +2,9 @@ use rakrs::client::{Client, DEFAULT_MTU}; #[async_std::main] async fn main() { - let mut client = Client::new(8, DEFAULT_MTU); + let mut client = Client::new(11, DEFAULT_MTU); - client.connect("127.0.0.1:19132").await.unwrap(); + client.connect("135.148.137.229:19132").await.unwrap(); loop { let pk = client.recv().await.unwrap(); diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 90f877c..4bc1957 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -31,6 +31,7 @@ use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, SessionInfoRequest, }; +use crate::protocol::packet::online::ConnectedPong; use crate::protocol::packet::online::{ConnectionRequest, NewConnection, OnlinePacket}; use crate::protocol::packet::Packet; use crate::protocol::packet::PacketId; @@ -63,6 +64,8 @@ macro_rules! match_ids { Ok(l) => len = l }; + rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + if ids.contains(&recv_buf[0]) { pk = Some(recv_buf[..len].to_vec()); break; @@ -96,9 +99,13 @@ macro_rules! expect_reply { Ok(l) => len = l, }; + rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + if let Ok(packet) = <$reply>::compose(&mut recv_buf[1..len], &mut 0) { pk = Some(packet); break; + } else { + rakrs_debug!(true, "[CLIENT] Failed to parse packet!"); } } @@ -165,7 +172,10 @@ impl ClientHandshake { update_state!(shared_state, HandshakeStatus::Opening); + rakrs_debug!(true, "[CLIENT] Sending OpenConnectRequest to server..."); + if !send_packet(&socket, connect_request.into()).await { + rakrs_debug!(true, "[CLIENT] Failed sending OpenConnectRequest to server!"); update_state!(true, shared_state, HandshakeStatus::Failed); }; @@ -197,6 +207,8 @@ impl ClientHandshake { return; } + rakrs_debug!(true, "[CLIENT] Received OpenConnectReply from server!"); + let session_info = SessionInfoRequest { magic: Magic::new(), address: socket.peer_addr().unwrap(), @@ -204,6 +216,8 @@ impl ClientHandshake { client_id: id, }; + rakrs_debug!(true, "[CLIENT] Sending SessionInfoRequest to server..."); + update_state!(shared_state, HandshakeStatus::SessionOpen); if !send_packet(&socket, session_info.into()).await { @@ -222,6 +236,8 @@ impl ClientHandshake { update_state!(true, shared_state, HandshakeStatus::Failed); } + rakrs_debug!(true, "[CLIENT] Received SessionInfoReply from server!"); + // create a temporary sendq let mut send_q = SendQueue::new( mtu, @@ -235,20 +251,22 @@ impl ClientHandshake { let connect_request = ConnectionRequest { time: current_epoch() as i64, client_id: id, + security: false, }; if let Err(_) = send_q - .insert( - Packet::from(connect_request).parse().unwrap(), - Reliability::ReliableOrd, - true, - None, + .send_packet( + connect_request.into(), + Reliability::Reliable, + true ) .await { update_state!(true, shared_state, HandshakeStatus::Failed); } + rakrs_debug!(true, "[CLIENT] Sent ConnectionRequest to server!"); + let mut buf: [u8; 2048] = [0; 2048]; loop { @@ -273,9 +291,34 @@ impl ClientHandshake { for mut raw_pk in raw_packets { let pk = Packet::compose(&mut raw_pk[..], &mut 0); + rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &raw_pk[..]); + if let Ok(pk) = pk { if pk.is_online() { match pk.get_online() { + OnlinePacket::ConnectedPing(pk) => { + println!("Received ConnectedPing from server!"); + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + + if let Err(_) = send_q + .send_packet( + response.into(), + Reliability::Reliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + + continue; + }, OnlinePacket::ConnectionAccept(pk) => { // send new incoming connection let new_incoming = NewConnection { @@ -287,9 +330,9 @@ impl ClientHandshake { if let Err(_) = send_q .insert( Packet::from(new_incoming).parse().unwrap(), - Reliability::ReliableOrd, + Reliability::Reliable, true, - None, + Some(0), ) .await { @@ -339,10 +382,11 @@ impl Future for ClientHandshake { } async fn send_packet(socket: &Arc, packet: Packet) -> bool { - if let Err(e) = socket.send(&mut packet.parse().unwrap()[..]).await { + if let Err(e) = socket.send_to(&mut packet.parse().unwrap()[..], socket.peer_addr().unwrap()).await { rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); return false; } else { + rakrs_debug!(true, "[CLIENT] Sent payload to server!"); return true; } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 9d9ffeb..eb9293b 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -44,14 +44,19 @@ use crate::{ frame::FramePacket, packet::{ online::{ConnectedPing, ConnectedPong, OnlinePacket}, - Packet, + Packet, offline::{UnconnectedPing, OfflinePacket}, }, - reliability::Reliability, + reliability::Reliability, Magic, }, rakrs_debug, server::{current_epoch, PossiblySocketAddr}, }; +#[cfg(feature = "mcpe")] +use crate::protocol::mcpe::UnconnectedPong; +#[cfg(not(feature = "mcpe"))] +use crate::protocol::packet::offline::UnconnectedPong; + pub const DEFAULT_MTU: u16 = 1400; use self::handshake::{ClientHandshake, HandshakeStatus}; @@ -133,12 +138,13 @@ impl Client { } }; - if timeout(Duration::from_secs(10), sock.connect(address)) - .await - .is_err() - { + rakrs_debug!(true, "[CLIENT] Attempting to connect to address: {}", address); + + let res = timeout(Duration::from_secs(5), sock.connect(address)).await; + + if res.is_err() { rakrs_debug!("[CLIENT] Failed to connect to address"); - self.close().await; + self.closed.store(false, std::sync::atomic::Ordering::Relaxed); return Err(ClientError::Killed); } @@ -158,6 +164,9 @@ impl Client { let closer = self.closed.clone(); + Self::ping(socket.clone()).await?; + + rakrs_debug!(true, "[CLIENT] Starting connection handshake"); // before we even start the connection, we need to complete the handshake let handshake = ClientHandshake::new(socket.clone(), self.id as i64, self.version, self.mtu, 5).await; @@ -167,6 +176,8 @@ impl Client { return Err(ClientError::Killed); } + rakrs_debug!(true, "[CLIENT] Handshake completed!"); + let socket_task = task::spawn(async move { let mut buf: [u8; 2048] = [0; 2048]; @@ -346,6 +357,49 @@ impl Client { } } + pub async fn ping(socket: Arc) -> Result { + let mut buf: [u8; 2048] = [0; 2048]; + let unconnected_ping = UnconnectedPing { + timestamp: current_epoch(), + magic: Magic::new(), + client_id: rand::random::(), + }; + + if let Err(_) = socket.send(&Packet::from(unconnected_ping.clone()).parse().unwrap()[..]).await { + rakrs_debug!(true, "[CLIENT] Failed to send ping packet!"); + return Err(ClientError::ServerOffline); + } + + loop { + rakrs_debug!(true, "[CLIENT] Waiting for pong packet..."); + if let Ok(recvd) = timeout(Duration::from_millis(10000), socket.recv(&mut buf)).await { + match recvd { + Ok(l) => { + let packet = Packet::compose(&mut buf[..l], &mut 0).unwrap(); + if packet.is_offline() { + match packet.get_offline() { + OfflinePacket::UnconnectedPong(pk) => { + rakrs_debug!(true, "[CLIENT] Recieved pong packet!"); + return Ok(pk); + } + _ => { + + } + }; + } + }, + Err(_) => { + rakrs_debug!(true, "[CLIENT] Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + } + } else { + rakrs_debug!(true, "[CLIENT] Ping Failed, server did not respond!"); + return Err(ClientError::ServerOffline); + } + } + } + async fn push_task(&mut self, task: JoinHandle<()>) { self.tasks.lock().await.push(task); } @@ -546,7 +600,7 @@ impl Client { loop { sleep(Duration::from_millis(50)).await; - if closer.load(std::sync::atomic::Ordering::Relaxed) { + if closer.load(std::sync::atomic::Ordering::Relaxed) == true { rakrs_debug!(true, "[CLIENT] Connect tick task closed"); break; } diff --git a/src/error/client.rs b/src/error/client.rs index fbc04f1..1b04145 100644 --- a/src/error/client.rs +++ b/src/error/client.rs @@ -9,5 +9,6 @@ pub enum ClientError { IncompatibleProtocolVersion, Killed, Reset, + ServerOffline, SendQueueError(SendQueueError), } diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index d645932..d5d021b 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -59,6 +59,7 @@ packet_id!(ConnectedPong, 0x03); pub struct ConnectionRequest { pub client_id: i64, pub time: i64, + pub security: bool, } packet_id!(ConnectionRequest, 0x09); From a449104a5087547f3b8b6f5c863025afeec8ffc9 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 11 Jun 2023 22:43:12 -0500 Subject: [PATCH 45/75] feat: client fixes and format [ci skip] --- src/client/handshake.rs | 39 ++++++++++++++++++--------------------- src/client/mod.rs | 35 +++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 4bc1957..3432e54 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -5,12 +5,7 @@ use std::sync::Mutex; use async_std::{ future::Future, net::UdpSocket, - task::{ - Context, - Poll, - Waker, - self - }, + task::{self, Context, Poll, Waker}, }; use binary_utils::Streamable; @@ -64,7 +59,7 @@ macro_rules! match_ids { Ok(l) => len = l }; - rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + // rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); if ids.contains(&recv_buf[0]) { pk = Some(recv_buf[..len].to_vec()); @@ -99,7 +94,7 @@ macro_rules! expect_reply { Ok(l) => len = l, }; - rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + // rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); if let Ok(packet) = <$reply>::compose(&mut recv_buf[1..len], &mut 0) { pk = Some(packet); @@ -175,7 +170,10 @@ impl ClientHandshake { rakrs_debug!(true, "[CLIENT] Sending OpenConnectRequest to server..."); if !send_packet(&socket, connect_request.into()).await { - rakrs_debug!(true, "[CLIENT] Failed sending OpenConnectRequest to server!"); + rakrs_debug!( + true, + "[CLIENT] Failed sending OpenConnectRequest to server!" + ); update_state!(true, shared_state, HandshakeStatus::Failed); }; @@ -255,11 +253,7 @@ impl ClientHandshake { }; if let Err(_) = send_q - .send_packet( - connect_request.into(), - Reliability::Reliable, - true - ) + .send_packet(connect_request.into(), Reliability::Reliable, true) .await { update_state!(true, shared_state, HandshakeStatus::Failed); @@ -291,8 +285,6 @@ impl ClientHandshake { for mut raw_pk in raw_packets { let pk = Packet::compose(&mut raw_pk[..], &mut 0); - rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &raw_pk[..]); - if let Ok(pk) = pk { if pk.is_online() { match pk.get_online() { @@ -318,7 +310,7 @@ impl ClientHandshake { } continue; - }, + } OnlinePacket::ConnectionAccept(pk) => { // send new incoming connection let new_incoming = NewConnection { @@ -328,11 +320,10 @@ impl ClientHandshake { timestamp: pk.timestamp, }; if let Err(_) = send_q - .insert( - Packet::from(new_incoming).parse().unwrap(), + .send_packet( + new_incoming.into(), Reliability::Reliable, true, - Some(0), ) .await { @@ -382,7 +373,13 @@ impl Future for ClientHandshake { } async fn send_packet(socket: &Arc, packet: Packet) -> bool { - if let Err(e) = socket.send_to(&mut packet.parse().unwrap()[..], socket.peer_addr().unwrap()).await { + if let Err(e) = socket + .send_to( + &mut packet.parse().unwrap()[..], + socket.peer_addr().unwrap(), + ) + .await + { rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); return false; } else { diff --git a/src/client/mod.rs b/src/client/mod.rs index eb9293b..e45d351 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -43,10 +43,12 @@ use crate::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, packet::{ + offline::{OfflinePacket, UnconnectedPing}, online::{ConnectedPing, ConnectedPong, OnlinePacket}, - Packet, offline::{UnconnectedPing, OfflinePacket}, + Packet, }, - reliability::Reliability, Magic, + reliability::Reliability, + Magic, }, rakrs_debug, server::{current_epoch, PossiblySocketAddr}, @@ -138,13 +140,18 @@ impl Client { } }; - rakrs_debug!(true, "[CLIENT] Attempting to connect to address: {}", address); + rakrs_debug!( + true, + "[CLIENT] Attempting to connect to address: {}", + address + ); let res = timeout(Duration::from_secs(5), sock.connect(address)).await; if res.is_err() { rakrs_debug!("[CLIENT] Failed to connect to address"); - self.closed.store(false, std::sync::atomic::Ordering::Relaxed); + self.closed + .store(false, std::sync::atomic::Ordering::Relaxed); return Err(ClientError::Killed); } @@ -365,7 +372,10 @@ impl Client { client_id: rand::random::(), }; - if let Err(_) = socket.send(&Packet::from(unconnected_ping.clone()).parse().unwrap()[..]).await { + if let Err(_) = socket + .send(&Packet::from(unconnected_ping.clone()).parse().unwrap()[..]) + .await + { rakrs_debug!(true, "[CLIENT] Failed to send ping packet!"); return Err(ClientError::ServerOffline); } @@ -382,12 +392,10 @@ impl Client { rakrs_debug!(true, "[CLIENT] Recieved pong packet!"); return Ok(pk); } - _ => { - - } + _ => {} }; } - }, + } Err(_) => { rakrs_debug!(true, "[CLIENT] Failed to recieve anything on netowrk channel, is there a sender?"); continue; @@ -492,7 +500,7 @@ impl Client { if let Err(_) = q .send_packet( response.into(), - Reliability::Reliable, + Reliability::Unreliable, true, ) .await @@ -510,6 +518,13 @@ impl Client { "[CLIENT] Recieved pong packet!" ); } + OnlinePacket::Disconnect(_) => { + rakrs_debug!( + true, + "[CLIENT] Recieved disconnect packet!" + ); + break; + } _ => {} }; From f61f51d0af02290394e12bc8acf3d64b04d7f21a Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 16 Jun 2023 13:40:27 -0500 Subject: [PATCH 46/75] chore: fix NewIncomingConnection, breaks openconnectrequest, (fix next) --- examples/async-std/client/src/main.rs | 7 +-- raknet-test/.cargo/config.toml | 2 + raknet-test/Cargo.toml | 12 +++++ raknet-test/src/main.rs | 69 +++++++++++++++++++++++++++ src/client/handshake.rs | 15 +++++- src/protocol/packet/online.rs | 36 +++++++++++++- 6 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 raknet-test/.cargo/config.toml create mode 100644 raknet-test/Cargo.toml create mode 100644 raknet-test/src/main.rs diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index fce75d9..48a2905 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -1,10 +1,11 @@ use rakrs::client::{Client, DEFAULT_MTU}; +use std::net::ToSocketAddrs; #[async_std::main] async fn main() { - let mut client = Client::new(11, DEFAULT_MTU); - - client.connect("135.148.137.229:19132").await.unwrap(); + let mut client = Client::new(10, DEFAULT_MTU); + let mut addr = "versai.pro:19132".to_socket_addrs().unwrap(); + client.connect(addr.next().unwrap()).await.unwrap(); loop { let pk = client.recv().await.unwrap(); diff --git a/raknet-test/.cargo/config.toml b/raknet-test/.cargo/config.toml new file mode 100644 index 0000000..16f1e73 --- /dev/null +++ b/raknet-test/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/raknet-test/Cargo.toml b/raknet-test/Cargo.toml new file mode 100644 index 0000000..77ee6eb --- /dev/null +++ b/raknet-test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "raknet-test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-std = { version = "1.12.0", features = [ "attributes" ] } +console-subscriber = "0.1.8" +rakrs = { path = "../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } +tokio = "1.23.0" diff --git a/raknet-test/src/main.rs b/raknet-test/src/main.rs new file mode 100644 index 0000000..2ae75be --- /dev/null +++ b/raknet-test/src/main.rs @@ -0,0 +1,69 @@ +use rakrs::Listener; +use rakrs::Motd; +use rakrs::connection::Connection; +use rakrs::mcpe; +use rakrs::mcpe::motd::Gamemode; +use rakrs::server::event::ServerEvent; +use rakrs::server::event::ServerEventResponse; + + +#[tokio::main] +async fn main() { + console_subscriber::init(); + let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + // let inner = server.recv_evnt.clone(); + server.motd.name = "RakNet Rust!".to_string(); + server.motd.gamemode = Gamemode::Survival; + + server.start().await.unwrap(); + + loop { + // let mut recvr = inner.lock().await; + // tokio::select! { + // ev = recvr.recv() => { + // match ev { + // Some((event, shoot)) => { + // match event { + // ServerEvent::RefreshMotdRequest(_addr, mut motd) => { + // // force update the motd!!! + // motd.name = "You are a loser!!!!!!!!!".to_string(); + // motd.player_max = 14_903; + // motd.player_count = 0; + // shoot.send(ServerEventResponse::RefreshMotd(motd)).unwrap(); + // }, + // _ => { + // println!("Got a response!"); + // // YOU NEED TO ALWAYS REPLY TO EVENTS + // shoot.send(ServerEventResponse::Acknowledged); + // } + // } + // }, + // None => { + // println!("Error!"); + // break; + // } + // } + // }, + // connection = server.accept() => { + // tokio::task::spawn(handle(connection.unwrap())); + // } + + let conn = server.accept().await; + async_std::task::spawn(handle(conn.unwrap())); + // } + } +} + +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed() { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); + } + // conn.tick().await; + } +} \ No newline at end of file diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 3432e54..7965a97 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -315,7 +315,18 @@ impl ClientHandshake { // send new incoming connection let new_incoming = NewConnection { server_address: socket.peer_addr().unwrap(), - system_address: socket.local_addr().unwrap(), + system_address: vec![ + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + ], request_time: pk.request_time, timestamp: pk.timestamp, }; @@ -381,6 +392,8 @@ async fn send_packet(socket: &Arc, packet: Packet) -> bool { .await { rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); + rakrs_debug!(" -> PAYLOAD: {:?}", &packet.parse().unwrap()[..]); + rakrs_debug!(" -> PACKET: {:?}", packet); return false; } else { rakrs_debug!(true, "[CLIENT] Sent payload to server!"); diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index d5d021b..5eb73c1 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -1,3 +1,4 @@ +use std::io::Cursor; use std::io::Write; use std::net::IpAddr; use std::net::Ipv4Addr; @@ -6,6 +7,7 @@ use std::net::SocketAddr; use binary_utils::error::BinaryError; use binary_utils::*; use byteorder::BigEndian; +use byteorder::ReadBytesExt; use byteorder::WriteBytesExt; use super::Packet; @@ -110,12 +112,12 @@ packet_id!(ConnectionAccept, 0x10); /// Going to be completely Honest here, I have no idea what this is used for right now, /// even after reading the source code. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug)] pub struct NewConnection { /// The external IP Address of the server. pub server_address: SocketAddr, /// The internal IP Address of the server. - pub system_address: SocketAddr, + pub system_address: Vec, /// The time of the timestamp the client sent with `ConnectionRequest`. pub request_time: i64, /// The time on the server. @@ -123,6 +125,36 @@ pub struct NewConnection { } packet_id!(NewConnection, 0x13); +impl Streamable for NewConnection { + fn parse(&self) -> Result, BinaryError> { + let mut stream = Vec::new(); + stream.write_all(&self.server_address.parse()?[..])?; + for address in &self.system_address { + stream.write_all(&address.parse()?[..])?; + } + stream.write_i64::(self.request_time)?; + stream.write_i64::(self.timestamp)?; + Ok(stream) + } + + fn compose(source: &[u8], position: &mut usize) -> Result { + let server_address = SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120); + let mut stream = Cursor::new(source); + let mut system_address = Vec::new(); + for _ in 0..10 { + system_address.push(SocketAddr::compose(source, position)?); + } + let request_time = stream.read_i64::()?; + let timestamp = stream.read_i64::()?; + Ok(Self { + server_address, + system_address, + request_time, + timestamp, + }) + } +} + /// A disconnect notification. Tells the client to disconnect. #[derive(Clone, Debug, BinaryStream)] pub struct Disconnect {} From 4d10ef7dd19fb9746af382468d1971d0e9770f3f Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 16 Jun 2023 18:59:58 -0500 Subject: [PATCH 47/75] feat: fix client --- examples/async-std/client/src/main.rs | 2 +- .../async-std/server}/.cargo/config.toml | 0 examples/async-std/server/Cargo.toml | 5 ++- examples/async-std/server/src/main.rs | 32 +++++++++++++++++-- examples/tokio/server/.cargo/config.toml | 2 ++ .../tokio/server}/Cargo.toml | 0 .../tokio/server}/src/main.rs | 0 src/client/handshake.rs | 9 ++++-- src/client/mod.rs | 21 ++++++++---- 9 files changed, 57 insertions(+), 14 deletions(-) rename {raknet-test => examples/async-std/server}/.cargo/config.toml (100%) create mode 100644 examples/tokio/server/.cargo/config.toml rename {raknet-test => examples/tokio/server}/Cargo.toml (100%) rename {raknet-test => examples/tokio/server}/src/main.rs (100%) diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index 48a2905..640e1fe 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -4,7 +4,7 @@ use std::net::ToSocketAddrs; #[async_std::main] async fn main() { let mut client = Client::new(10, DEFAULT_MTU); - let mut addr = "versai.pro:19132".to_socket_addrs().unwrap(); + let mut addr = "zeqa.net:19132".to_socket_addrs().unwrap(); client.connect(addr.next().unwrap()).await.unwrap(); loop { diff --git a/raknet-test/.cargo/config.toml b/examples/async-std/server/.cargo/config.toml similarity index 100% rename from raknet-test/.cargo/config.toml rename to examples/async-std/server/.cargo/config.toml diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml index dd022ab..5cf106d 100644 --- a/examples/async-std/server/Cargo.toml +++ b/examples/async-std/server/Cargo.toml @@ -1,8 +1,11 @@ [package] -name = "server" +name = "raknet-server-async-std" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-std = { version = "1.12.0", features = [ "attributes" ] } +console-subscriber = "0.1.8" +rakrs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs index e7a11a9..5ee550a 100644 --- a/examples/async-std/server/src/main.rs +++ b/examples/async-std/server/src/main.rs @@ -1,3 +1,31 @@ -fn main() { - println!("Hello, world!"); +use rakrs::connection::Connection; +use rakrs::mcpe::motd::Gamemode; +use rakrs::Listener; + +#[async_std::main] +async fn main() { + // console_subscriber::init(); + let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + server.motd.name = "RakNet Rust!".to_string(); + server.motd.gamemode = Gamemode::Survival; + + server.start().await.unwrap(); + + loop { + let conn = server.accept().await; + async_std::task::spawn(handle(conn.unwrap())); + } +} + +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed() { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); + } + } } diff --git a/examples/tokio/server/.cargo/config.toml b/examples/tokio/server/.cargo/config.toml new file mode 100644 index 0000000..16f1e73 --- /dev/null +++ b/examples/tokio/server/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/raknet-test/Cargo.toml b/examples/tokio/server/Cargo.toml similarity index 100% rename from raknet-test/Cargo.toml rename to examples/tokio/server/Cargo.toml diff --git a/raknet-test/src/main.rs b/examples/tokio/server/src/main.rs similarity index 100% rename from raknet-test/src/main.rs rename to examples/tokio/server/src/main.rs diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 7965a97..8658086 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -289,7 +289,10 @@ impl ClientHandshake { if pk.is_online() { match pk.get_online() { OnlinePacket::ConnectedPing(pk) => { - println!("Received ConnectedPing from server!"); + rakrs_debug!( + true, + "[CLIENT] Received ConnectedPing from server!" + ); let response = ConnectedPong { ping_time: pk.time, pong_time: current_epoch() as i64, @@ -392,8 +395,8 @@ async fn send_packet(socket: &Arc, packet: Packet) -> bool { .await { rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); - rakrs_debug!(" -> PAYLOAD: {:?}", &packet.parse().unwrap()[..]); - rakrs_debug!(" -> PACKET: {:?}", packet); + rakrs_debug!(true, " -> PAYLOAD: {:?}", &packet.parse().unwrap()[..]); + rakrs_debug!(true, " -> PACKET: {:?}", packet); return false; } else { rakrs_debug!(true, "[CLIENT] Sent payload to server!"); diff --git a/src/client/mod.rs b/src/client/mod.rs index e45d351..eb57569 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -436,7 +436,7 @@ impl Client { let recv_time = self.recv_time.clone(); let r = task::spawn(async move { - loop { + 'task_loop: loop { let pk_recv = net_recv.lock().await.recv().await; if closed.load(std::sync::atomic::Ordering::Relaxed) == true { rakrs_debug!("[CLIENT] (recv_task) The internal network recieve channel has been killed."); @@ -487,7 +487,7 @@ impl Client { let buffers = recv_q.flush(); - for mut pk_buf in buffers { + 'buf_loop: for mut pk_buf in buffers { if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { if packet.is_online() { match packet.get_online() { @@ -510,6 +510,7 @@ impl Client { "[CLIENT] Failed to send pong packet!" ); } + continue 'buf_loop; } OnlinePacket::ConnectedPong(_) => { // todo: add ping time to client @@ -523,11 +524,17 @@ impl Client { true, "[CLIENT] Recieved disconnect packet!" ); - break; + break 'task_loop; } _ => {} }; + rakrs_debug!( + true, + "[CLIENT] Processing fault packet... {:#?}", + packet + ); + if let Err(_) = internal_sender.send(pk_buf).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } @@ -536,10 +543,10 @@ impl Client { rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); } } else { - rakrs_debug!( - true, - "[CLIENT] Failed to send packet to client (parsing failed)!", - ); + // we send this packet + if let Err(_) = internal_sender.send(pk_buf).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } } } } From 718986136020532298ed458df54964d41f43f0e7 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 16 Jun 2023 19:01:14 -0500 Subject: [PATCH 48/75] chore: bump version & prepare release v3 [ci skip] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e8713aa..2c574ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rakrs" -version = "0.3.0-rc.4" +version = "0.3.0-rc.5" authors = ["Bavfalcon9 "] edition = "2021" From 41cf4ac4cd4b3fc2d3138038a01bf37eeeee91e5 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 16 Jun 2023 20:12:42 -0500 Subject: [PATCH 49/75] chore: fix head --- README.md | 116 ++++++++++++++++++-------- examples/async-std/client/Cargo.toml | 2 +- examples/async-std/client/src/main.rs | 2 +- examples/async-std/server/Cargo.toml | 2 +- examples/async-std/server/src/main.rs | 6 +- examples/tokio/server/Cargo.toml | 2 +- examples/tokio/server/src/main.rs | 49 ++--------- src/lib.rs | 109 ++++++++++++++++++++++++ 8 files changed, 206 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index a82b8b1..f410b1f 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,100 @@ -# RakNet +# rak-rs -A fully functional RakNet implementation in rust, asynchronously driven. +A fully functional RakNet implementation in pure rust, asynchronously driven. -### Installation +## Getting Started -By default `rakrs` will use `async_std` feature, which in turn, utilizes the `async_std` crate. To use `tokio` you will need to add it to cargo features as `async_tokio`. +RakNet (rak-rs) is available on [crates.io](), to use it, add the following to your `Cargo.toml`: -Using the tokio library with the `mcpe` feature may look like the following: +```toml +[dependencies] +rakrs = "0.3.0" +``` + +## Features + +This RakNet implementation comes with 3 primary features, `async_std`, `async_tokio` and `mcpe`. However, by default, only `async_std` is enabled, and `mcpe` requires you to modify your `Cargo.toml`. + +If you wish to use these features, add them to your `Cargo.toml` as seen below: ```toml -rakrs = { version = "0.3.0", features = [ "mcpe", "async_std" ], default-features = false } +[dependencies] +rak_rs = { version = "0.3.0", default-features = false, features = [ "async_tokio", "mcpe" ] } ``` + + +rak-rs also provides the following modules: + +- [`rak_rs::client`](https://docs.rs/rak-rs/latest/rak-rs/client) - A client implementation of RakNet, allowing you to connect to a RakNet server. +- [`rak_rs::connection`](https://docs.rs/rak-rs/latest/rak-rs/client) - A bare-bones implementation of a Raknet peer, this is mainly used for types. +- [`rak_rs::error`](https://docs.rs/rak-rs/latest/rak-rs/error) - A module with errors that both the Client and Server can respond with. +- [`rak_rs::protocol`](https://docs.rs/rak-rs/latest/rak-rs/protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. +- [`rak_rs::server`](https://docs.rs/rak-rs/latest/rak-rs/server) - The base server implementation of RakNet. +- [`rak_rs::utils`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. + +# Client + +The `client` module provides a way for you to interact with RakNet servers with code. + +**Example:** + ```rust -// Create a server -use rakrs::Listener; -use rakrs::util::handle; -use rakrs::util::mcpe; - -async fn my_handler(conn: RakConnection, mut stream: RakStream) { - // The `conn.recv()` method constructs a `Packet` from the stream - // Which becomes usable later. - while let Some(packet) = conn.recv(&mut stream).await { - if let Ok(packet) = packet { - // RakNet packets are sent here! We can send some back as well! - let hello = raknet::protocol::online::ConnectedPing::new(); - conn.send(hello.into(), Reliability::Reliable).await; - } +use rak_rs::client::{Client, DEFAULT_MTU}; +use std::net::ToSocketAddrs; + +#[async_std::main] +async fn main() { + let version: u8 = 10; + let addr = "my_server.net:19132".to_socket_addrs().unwrap(); + let mut client = Client::new(version, DEFAULT_MTU); + + client.connect(addr.next().unwrap()).await.unwrap(); + + // receive packets + loop { + let packet = client.recv().await.unwrap(); + + println!("Received a packet! {:?}", packet); + + client.send_ord(vec![254, 0, 1, 1], Some(1)); } } -async fn main() { - // Bind to a socket and allow minecraft protocol - let mut server = Listener::bind("0.0.0.0:19132", true).await; - server.motd.name = "Rust Bedrock Minecraft server"; - server.motd.player_count = 100; - server.motd.player_max = 200; - server.motd.gamemode = mcpe::Gamemode::Survival; +``` + +# Server - // Begin listening to incoming connections - server.start().await; +A RakNet server implementation in pure rust. + +**Example:** + +```rust +use rakrs::connection::Connection; +use rakrs::Listener; +use rakrs:: + +#[async_std::main] +async fn main() { + let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + server.start().await.unwrap(); loop { - let conn = server.accept().await.unwrap(); + let conn = server.accept().await; + async_std::task::spawn(handle(conn.unwrap())); + } +} - // You can use the default handler, or create your own - // the default handler - tokio::spawn(handle(conn, stream)); - // your own handler - tokio::spawn(my_handler(conn, stream)); +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed() { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("Got a connection packet {:?} ", pk); + } } } ``` \ No newline at end of file diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml index 38913b2..da2b064 100644 --- a/examples/async-std/client/Cargo.toml +++ b/examples/async-std/client/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -rakrs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file +rak_rs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index 640e1fe..1bc1973 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -1,4 +1,4 @@ -use rakrs::client::{Client, DEFAULT_MTU}; +use rak_rs::client::{Client, DEFAULT_MTU}; use std::net::ToSocketAddrs; #[async_std::main] diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml index 5cf106d..93ac061 100644 --- a/examples/async-std/server/Cargo.toml +++ b/examples/async-std/server/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "attributes" ] } console-subscriber = "0.1.8" -rakrs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file +rak_rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs index 5ee550a..c99e558 100644 --- a/examples/async-std/server/src/main.rs +++ b/examples/async-std/server/src/main.rs @@ -1,6 +1,6 @@ -use rakrs::connection::Connection; -use rakrs::mcpe::motd::Gamemode; -use rakrs::Listener; +use rak_rs::connection::Connection; +use rak_rs::mcpe::motd::Gamemode; +use rak_rs::Listener; #[async_std::main] async fn main() { diff --git a/examples/tokio/server/Cargo.toml b/examples/tokio/server/Cargo.toml index 77ee6eb..ebea5d4 100644 --- a/examples/tokio/server/Cargo.toml +++ b/examples/tokio/server/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "attributes" ] } console-subscriber = "0.1.8" -rakrs = { path = "../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } +rak_rs = { path = "../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } tokio = "1.23.0" diff --git a/examples/tokio/server/src/main.rs b/examples/tokio/server/src/main.rs index 2ae75be..7d81efd 100644 --- a/examples/tokio/server/src/main.rs +++ b/examples/tokio/server/src/main.rs @@ -1,10 +1,10 @@ -use rakrs::Listener; -use rakrs::Motd; -use rakrs::connection::Connection; -use rakrs::mcpe; -use rakrs::mcpe::motd::Gamemode; -use rakrs::server::event::ServerEvent; -use rakrs::server::event::ServerEventResponse; +use rak_rs::Listener; +use rak_rs::Motd; +use rak_rs::connection::Connection; +use rak_rs::mcpe; +use rak_rs::mcpe::motd::Gamemode; +use rak_rs::server::event::ServerEvent; +use rak_rs::server::event::ServerEventResponse; #[tokio::main] @@ -18,39 +18,8 @@ async fn main() { server.start().await.unwrap(); loop { - // let mut recvr = inner.lock().await; - // tokio::select! { - // ev = recvr.recv() => { - // match ev { - // Some((event, shoot)) => { - // match event { - // ServerEvent::RefreshMotdRequest(_addr, mut motd) => { - // // force update the motd!!! - // motd.name = "You are a loser!!!!!!!!!".to_string(); - // motd.player_max = 14_903; - // motd.player_count = 0; - // shoot.send(ServerEventResponse::RefreshMotd(motd)).unwrap(); - // }, - // _ => { - // println!("Got a response!"); - // // YOU NEED TO ALWAYS REPLY TO EVENTS - // shoot.send(ServerEventResponse::Acknowledged); - // } - // } - // }, - // None => { - // println!("Error!"); - // break; - // } - // } - // }, - // connection = server.accept() => { - // tokio::task::spawn(handle(connection.unwrap())); - // } - let conn = server.accept().await; - async_std::task::spawn(handle(conn.unwrap())); - // } + tokio::task::spawn(handle(conn.unwrap())); } } @@ -62,7 +31,7 @@ async fn handle(mut conn: Connection) { break; } if let Ok(pk) = conn.recv().await { - println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); + println!("Got a connection packet {:?} ", pk); } // conn.tick().await; } diff --git a/src/lib.rs b/src/lib.rs index 8cc5bc9..d535c2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,117 @@ +//! # rak-rs +//! +//! A fully functional RakNet implementation in pure rust, asynchronously driven. +//! +//! ## Getting Started +//! +//! RakNet (rak-rs) is available on [crates.io](), to use it, add the following to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! rakrs = "0.3.0" +//! ``` +//! +//! ## Features +//! +//! This RakNet implementation comes with 3 primary features, `async_std`, `async_tokio` and `mcpe`. However, by default, only `async_std` is enabled, and `mcpe` requires you to modify your `Cargo.toml`. +//! +//! If you wish to use these features, add them to your `Cargo.toml` as seen below: +//! +//! ```toml +//! [dependencies] +//! rak_rs = { version = "0.3.0", default-features = false, features = [ "async_tokio", "mcpe" ] } +//! ``` +//! +//! +//! +//! rak-rs also provides the following modules: +//! +//! - [`rak_rs::client`](https://docs.rs/rak-rs/latest/rak-rs/client) - A client implementation of RakNet, allowing you to connect to a RakNet server. +//! - [`rak_rs::connection`](https://docs.rs/rak-rs/latest/rak-rs/client) - A bare-bones implementation of a Raknet peer, this is mainly used for types. +//! - [`rak_rs::error`](https://docs.rs/rak-rs/latest/rak-rs/error) - A module with errors that both the Client and Server can respond with. +//! - [`rak_rs::protocol`](https://docs.rs/rak-rs/latest/rak-rs/protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. +//! - [`rak_rs::server`](https://docs.rs/rak-rs/latest/rak-rs/server) - The base server implementation of RakNet. +//! - [`rak_rs::utils`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. +//! +//! # Client +//! +//! The `client` module provides a way for you to interact with RakNet servers with code. +//! +//! **Example:** +//! +//! ```rust +//! use rak_rs::client::{Client, DEFAULT_MTU}; +//! use std::net::ToSocketAddrs; +//! +//! #[async_std::main] +//! async fn main() { +//! let version: u8 = 10; +//! let addr = "my_server.net:19132".to_socket_addrs().unwrap(); +//! let mut client = Client::new(version, DEFAULT_MTU); +//! +//! client.connect(addr.next().unwrap()).await.unwrap(); +//! +//! // receive packets +//! loop { +//! let packet = client.recv().await.unwrap(); +//! +//! println!("Received a packet! {:?}", packet); +//! +//! client.send_ord(vec![254, 0, 1, 1], Some(1)); +//! } +//! } +//! +//! ``` +//! +//! # Server +//! +//! A RakNet server implementation in pure rust. +//! +//! **Example:** +//! +//! ```rust +//! use rakrs::connection::Connection; +//! use rakrs::Listener; +//! use rakrs:: +//! +//! #[async_std::main] +//! async fn main() { +//! let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +//! server.start().await.unwrap(); +//! +//! loop { +//! let conn = server.accept().await; +//! async_std::task::spawn(handle(conn.unwrap())); +//! } +//! } +//! +//! async fn handle(mut conn: Connection) { +//! loop { +//! // keeping the connection alive +//! if conn.is_closed() { +//! println!("Connection closed!"); +//! break; +//! } +//! if let Ok(pk) = conn.recv().await { +//! println!("Got a connection packet {:?} ", pk); +//! } +//! } +//! } +//! ``` +/// A client implementation of RakNet, allowing you to connect to a RakNet server. pub mod client; +/// The connection implementation of RakNet, allowing you to send and receive packets. +/// This is barebones, and you should use the client or server implementations instead, this is mainly +/// used internally. pub mod connection; +/// The error implementation of RakNet, allowing you to handle errors. pub mod error; +/// The packet implementation of RakNet. +/// This is a lower level implementation responsible for serializing and deserializing packets. pub mod protocol; +/// The server implementation of RakNet, allowing you to create a RakNet server. pub mod server; +/// Utilties for RakNet, like epoch time. pub mod util; pub use protocol::mcpe::{self, motd::Motd}; From 117f400470476f83d2c239c0685f2dd1a9631aa4 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 16 Jun 2023 20:19:11 -0500 Subject: [PATCH 50/75] chore: fix name [ci skip] --- Cargo.toml | 2 +- examples/async-std/client/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c574ea..0f08af3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rakrs" +name = "rak-rs" version = "0.3.0-rc.5" authors = ["Bavfalcon9 "] edition = "2021" diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml index da2b064..a56f51f 100644 --- a/examples/async-std/client/Cargo.toml +++ b/examples/async-std/client/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -rak_rs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file From 226e53bbd8b899a0d61cc4b50823089484f1fffe Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Sun, 18 Jun 2023 21:53:41 -0500 Subject: [PATCH 51/75] chore: locales --- examples/async-std/client/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index 1bc1973..0880256 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -1,5 +1,5 @@ use rak_rs::client::{Client, DEFAULT_MTU}; -use std::net::ToSocketAddrs; +use std::{net::ToSocketAddrs, vec}; #[async_std::main] async fn main() { @@ -10,5 +10,6 @@ async fn main() { loop { let pk = client.recv().await.unwrap(); println!("Received packet: {:?}", pk); + client.send_ord(&vec![ 254, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 ], 1).await.unwrap(); } } \ No newline at end of file From 549aa9bc4e516d72717717cf817265ff2c799c77 Mon Sep 17 00:00:00 2001 From: AndreasHGK Date: Tue, 4 Jul 2023 17:47:55 +0200 Subject: [PATCH 52/75] client/mod.rs: Remove unecessary &mut receivers --- src/client/mod.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index eb57569..7642c59 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -228,13 +228,13 @@ impl Client { } /// Updates the client state - pub async fn update_state(&mut self, new_state: ConnectionState) { + pub async fn update_state(&self, new_state: ConnectionState) { let mut state = self.state.lock().await; *state = new_state; } /// Todo: send disconnect packet. - pub async fn close(&mut self) { + pub async fn close(&self) { self.update_state(ConnectionState::Disconnecting).await; self.closed .store(true, std::sync::atomic::Ordering::Relaxed); @@ -247,7 +247,7 @@ impl Client { } } - pub async fn send_ord(&mut self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + pub async fn send_ord(&self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q @@ -268,7 +268,7 @@ impl Client { } } - pub async fn send_seq(&mut self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + pub async fn send_seq(&self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q @@ -290,7 +290,7 @@ impl Client { } pub async fn send( - &mut self, + &self, buffer: &[u8], reliability: Reliability, channel: u8, @@ -311,7 +311,7 @@ impl Client { } pub async fn send_immediate( - &mut self, + &self, buffer: &[u8], reliability: Reliability, channel: u8, @@ -331,7 +331,7 @@ impl Client { } } - pub async fn flush_ack(&mut self) { + pub async fn flush_ack(&self) { let mut send_q = self.send_queue.as_ref().unwrap().write().await; let mut recv_q = self.recv_queue.lock().await; // Flush the queue of acks and nacks, and respond to them @@ -351,7 +351,7 @@ impl Client { } } - pub async fn recv(&mut self) -> Result, RecvError> { + pub async fn recv(&self) -> Result, RecvError> { match self.internal_recv.recv().await { #[cfg(feature = "async_std")] Ok(packet) => Ok(packet), @@ -408,11 +408,11 @@ impl Client { } } - async fn push_task(&mut self, task: JoinHandle<()>) { + async fn push_task(&self, task: JoinHandle<()>) { self.tasks.lock().await.push(task); } - async fn init_recv_task(&mut self) -> Result, ClientError> { + async fn init_recv_task(&self) -> Result, ClientError> { let net_recv = match self.network_recv { Some(ref n) => n.clone(), None => { @@ -604,7 +604,7 @@ impl Client { /// This is an internal function that initializes the client connection. /// This is called by `Client::connect()`. async fn init_connect_tick( - &mut self, + &self, send_queue: Arc>, ) -> Result, ClientError> { // verify that the client is offline From 9c64dc74704137096c2057541ce9d855be1cc4b6 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 10:56:08 -0500 Subject: [PATCH 53/75] chore: attempt to fix nightly tests --- src/connection/queue/mod.rs | 4 ++-- src/connection/state.rs | 2 +- src/lib.rs | 4 ++-- src/server/mod.rs | 5 +++-- src/util/mod.rs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index a399279..dc32948 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -136,8 +136,8 @@ impl NetQueue for RecoveryQueue { /// within a reliable window time. /// /// Usage: -/// ```rust -/// use rakrs::connection::queue::OrderedQueue; +/// ```ignore +/// use rak_rs::connection::queue::OrderedQueue; /// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); /// // Insert a packet with the id of "1" /// ord_qu.insert(1, vec![0, 1]); diff --git a/src/connection/state.rs b/src/connection/state.rs index 0139db0..e56cb1d 100644 --- a/src/connection/state.rs +++ b/src/connection/state.rs @@ -36,7 +36,7 @@ pub enum ConnectionState { /// The session is not connected and is not trying to connect. /// During this state the session will be dropped. This state occurs when a client /// has completely stopped responding to packets or their socket is destroyed. - /// This is not the same as the [Disconnected](rakrs::conn::state::Disconnected) state. + /// This is not the same as the [Disconnected](rak_rs::conn::state::Disconnected) state. Offline, } diff --git a/src/lib.rs b/src/lib.rs index d535c2f..b974eb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ //! //! **Example:** //! -//! ```rust +//! ```ignore //! use rak_rs::client::{Client, DEFAULT_MTU}; //! use std::net::ToSocketAddrs; //! @@ -69,7 +69,7 @@ //! //! **Example:** //! -//! ```rust +//! ```ignore //! use rakrs::connection::Connection; //! use rakrs::Listener; //! use rakrs:: diff --git a/src/server/mod.rs b/src/server/mod.rs index 27e6937..4b853c5 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -175,8 +175,9 @@ impl Listener { } /// Starts the listener! - /// ```rust - /// use rakrs::server::Listener; + /// TODO + /// ```ignore + /// use rak_rs::server::Listener; /// async fn start() { /// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); /// diff --git a/src/util/mod.rs b/src/util/mod.rs index 2d9514c..c54764e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -54,7 +54,7 @@ impl_gen!(usize); /// /// Usage example: /// ```rust -/// use rakrs::util::CacheStore; +/// use rak_rs::util::CacheStore; /// /// let mut myStore: CacheStore> = CacheStore::new(); /// let myPacket = (0 as u8, vec![0, 0, 0, 1]); From 4f500a5fcb2fa43283292ebf7880ce715eac4a8d Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 11:25:52 -0500 Subject: [PATCH 54/75] chore(connection/mod.rs): remove mutability requirement --- src/client/mod.rs | 2 +- src/connection/mod.rs | 4 ++-- src/notify/mod.rs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/notify/mod.rs diff --git a/src/client/mod.rs b/src/client/mod.rs index 7642c59..f4923df 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,7 +6,7 @@ use std::{ atomic::{AtomicBool, AtomicU64}, Arc, }, - time::Duration, + time::Duration, task::Waker, }; #[cfg(feature = "async_tokio")] diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 75cdabf..06afd4a 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -239,7 +239,7 @@ impl Connection { /// pub async fn init_net_recv( &self, - mut net: Receiver>, + net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); @@ -527,7 +527,7 @@ impl Connection { /// Send a packet to the client. /// These will be sent next tick unless otherwise specified. - pub async fn send(&mut self, buffer: Vec, immediate: bool) -> Result<(), SendQueueError> { + pub async fn send(&self, buffer: Vec, immediate: bool) -> Result<(), SendQueueError> { let mut q = self.send_queue.write().await; if let Err(e) = q .insert(buffer, Reliability::ReliableOrd, immediate, Some(0)) diff --git a/src/notify/mod.rs b/src/notify/mod.rs new file mode 100644 index 0000000..332b41b --- /dev/null +++ b/src/notify/mod.rs @@ -0,0 +1,4 @@ +/// Notifies any task to wake up +pub struct Notify { + +} \ No newline at end of file From 7f3b616010eab01d5ab70bdd834c1a15b53229b2 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 11:27:20 -0500 Subject: [PATCH 55/75] fmt(client/mod.rs): run format --- src/client/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index f4923df..5109895 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,7 +6,8 @@ use std::{ atomic::{AtomicBool, AtomicU64}, Arc, }, - time::Duration, task::Waker, + task::Waker, + time::Duration, }; #[cfg(feature = "async_tokio")] From 4b993a6537d4695f0d7f1289d1d256c44f661a7f Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 15:01:44 -0500 Subject: [PATCH 56/75] feat: Minor improvements & timeouts --- examples/async-std/client/src/main.rs | 8 ++++++-- src/client/handshake.rs | 24 ++++++++++++++++++++---- src/client/mod.rs | 6 +++++- src/connection/state.rs | 5 +++++ src/protocol/frame.rs | 2 +- src/protocol/packet/offline.rs | 6 +++++- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index 0880256..de8dfc7 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -4,8 +4,12 @@ use std::{net::ToSocketAddrs, vec}; #[async_std::main] async fn main() { let mut client = Client::new(10, DEFAULT_MTU); - let mut addr = "zeqa.net:19132".to_socket_addrs().unwrap(); - client.connect(addr.next().unwrap()).await.unwrap(); + let mut addr = "ownagepe.com:19132".to_socket_addrs().unwrap(); + if let Err(_) = client.connect(addr.next().unwrap()).await { + // here you could attempt to retry, but in this case, we'll just exit + println!("Failed to connect to server!"); + return; + } loop { let pk = client.recv().await.unwrap(); diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 8658086..b61f0fd 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -1,13 +1,16 @@ use std::sync::Arc; use std::sync::Mutex; +use std::time::Duration; #[cfg(feature = "async_std")] use async_std::{ future::Future, net::UdpSocket, task::{self, Context, Poll, Waker}, + future::timeout, }; + use binary_utils::Streamable; #[cfg(feature = "async_tokio")] use std::future::Future; @@ -17,6 +20,7 @@ use std::task::{Context, Poll, Waker}; use tokio::{ net::UdpSocket, task::{self}, + time::{sleep, timeout}, }; use crate::connection::queue::send::SendQueue; @@ -49,9 +53,14 @@ macro_rules! match_ids { } let len: usize; - let rc = $socket.recv(&mut recv_buf).await; + let send_result = timeout(Duration::from_secs(5), $socket.recv(&mut recv_buf)).await; + + if (send_result.is_err()) { + rakrs_debug!(true, "[CLIENT] Failed to receive packet from server! Is it offline?"); + break; + } - match rc { + match send_result.unwrap() { Err(_) => { tries += 1; continue; @@ -84,9 +93,14 @@ macro_rules! expect_reply { } let len: usize; - let rc = $socket.recv(&mut recv_buf).await; + let send_result = timeout(Duration::from_secs(10), $socket.recv(&mut recv_buf)).await; + + if (send_result.is_err()) { + rakrs_debug!(true, "[CLIENT] Failed to receive packet from server! Is it offline?"); + break; + } - match rc { + match send_result.unwrap() { Err(_) => { tries += 1; continue; @@ -159,6 +173,8 @@ impl ClientHandshake { let shared_state = state.clone(); task::spawn(async move { + // todo: continously send untill we get a reply + // todo: we also need to decrease the MTU until we get a reply let connect_request = OpenConnectRequest { magic: Magic::new(), protocol: version, diff --git a/src/client/mod.rs b/src/client/mod.rs index 5109895..cbe9bf2 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -174,6 +174,7 @@ impl Client { Self::ping(socket.clone()).await?; + self.update_state(ConnectionState::Unidentified).await; rakrs_debug!(true, "[CLIENT] Starting connection handshake"); // before we even start the connection, we need to complete the handshake let handshake = @@ -183,6 +184,7 @@ impl Client { rakrs_debug!("Failed to complete handshake: {:?}", handshake); return Err(ClientError::Killed); } + self.update_state(ConnectionState::Identified).await; rakrs_debug!(true, "[CLIENT] Handshake completed!"); @@ -265,6 +267,7 @@ impl Client { } Ok(()) } else { + rakrs_debug!(true, "[CLIENT] Client is not connected! State: {:?}", self.state.lock().await); Err(ClientError::NotListening) } } @@ -609,9 +612,10 @@ impl Client { send_queue: Arc>, ) -> Result, ClientError> { // verify that the client is offline - if self.state.lock().await.is_available() { + if *self.state.lock().await != ConnectionState::Identified { return Err(ClientError::AlreadyOnline); } + self.update_state(ConnectionState::Connected).await; let closer = self.closed.clone(); let recv_queue = self.recv_queue.clone(); diff --git a/src/connection/state.rs b/src/connection/state.rs index e56cb1d..6891ec4 100644 --- a/src/connection/state.rs +++ b/src/connection/state.rs @@ -33,6 +33,10 @@ pub enum ConnectionState { /// the "Unconnected" state, hence "UnconnectedPing" Unidentified, + /// The session has been identified and is ready to be connected. + /// This is the state after a connection has been established. + Identified, + /// The session is not connected and is not trying to connect. /// During this state the session will be dropped. This state occurs when a client /// has completely stopped responding to packets or their socket is destroyed. @@ -87,6 +91,7 @@ impl std::fmt::Display for ConnectionState { Self::Disconnecting => write!(f, "Disconnecting"), Self::Disconnected => write!(f, "Disconnected"), Self::Unidentified => write!(f, "Unidentified"), + Self::Identified => write!(f, "Identified"), Self::Offline => write!(f, "Offline"), } } diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 3cb414b..7819f74 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -268,7 +268,7 @@ impl Streamable for Frame { // and order channel if self.reliability.is_sequenced_or_ordered() { stream.write_u24::(self.order_index.unwrap())?; - stream.write_u8(self.order_channel.unwrap())?; + stream.write_u8(self.order_channel.unwrap_or(0))?; } // check whether or not this frame is fragmented, if it is, write the fragment meta diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 3510d58..1ffd755 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -5,6 +5,8 @@ use binary_utils::error::BinaryError; use binary_utils::*; use byteorder::WriteBytesExt; +use crate::protocol::MAGIC; +use crate::protocol::RAKNET_HEADER_OVERHEAD; #[cfg(feature = "mcpe")] pub use crate::protocol::mcpe::UnconnectedPong; @@ -83,7 +85,9 @@ impl Streamable for OpenConnectRequest { .expect("Failed to parse open connect request"); stream.write_u8(self.protocol)?; // padding - for _ in 0..self.mtu_size { + // remove 28 bytes from the mtu size + let mtu_size = self.mtu_size - stream.len() as u16 - RAKNET_HEADER_OVERHEAD as u16; + for _ in 0..mtu_size { stream.write_u8(0)?; } Ok(stream) From 0ef68975075672343e8fdca002143d9b3723abc0 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 15:02:09 -0500 Subject: [PATCH 57/75] fmt(client/handshake): Formatting --- src/client/handshake.rs | 8 +++++--- src/client/mod.rs | 6 +++++- src/protocol/packet/offline.rs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/client/handshake.rs b/src/client/handshake.rs index b61f0fd..a4c5c5f 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -4,13 +4,12 @@ use std::time::Duration; #[cfg(feature = "async_std")] use async_std::{ + future::timeout, future::Future, net::UdpSocket, task::{self, Context, Poll, Waker}, - future::timeout, }; - use binary_utils::Streamable; #[cfg(feature = "async_tokio")] use std::future::Future; @@ -96,7 +95,10 @@ macro_rules! expect_reply { let send_result = timeout(Duration::from_secs(10), $socket.recv(&mut recv_buf)).await; if (send_result.is_err()) { - rakrs_debug!(true, "[CLIENT] Failed to receive packet from server! Is it offline?"); + rakrs_debug!( + true, + "[CLIENT] Failed to receive packet from server! Is it offline?" + ); break; } diff --git a/src/client/mod.rs b/src/client/mod.rs index cbe9bf2..07a0480 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -267,7 +267,11 @@ impl Client { } Ok(()) } else { - rakrs_debug!(true, "[CLIENT] Client is not connected! State: {:?}", self.state.lock().await); + rakrs_debug!( + true, + "[CLIENT] Client is not connected! State: {:?}", + self.state.lock().await + ); Err(ClientError::NotListening) } } diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 1ffd755..1f07d61 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -5,10 +5,10 @@ use binary_utils::error::BinaryError; use binary_utils::*; use byteorder::WriteBytesExt; -use crate::protocol::MAGIC; -use crate::protocol::RAKNET_HEADER_OVERHEAD; #[cfg(feature = "mcpe")] pub use crate::protocol::mcpe::UnconnectedPong; +use crate::protocol::MAGIC; +use crate::protocol::RAKNET_HEADER_OVERHEAD; use super::Packet; use super::PacketId; From 5747b0bbdfc56d2b4102d971aefc4bf778e07db9 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 15:40:50 -0500 Subject: [PATCH 58/75] chore: update dependencies --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0f08af3..4f96bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } +binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2", version = "0.2.2" } tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" From 17d605ffe1bd7ca50938cfade38006e264f157bc Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Tue, 4 Jul 2023 15:41:18 -0500 Subject: [PATCH 59/75] chore: update dependencies [ci skip] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4f96bca..1e9b10d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2", version = "0.2.2" } +binary-utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2", version = "0.2.2" } tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" From 021145e90f02d1b01f1ba4daab0603eca64883c6 Mon Sep 17 00:00:00 2001 From: john-bv Date: Tue, 8 Aug 2023 22:34:17 -0500 Subject: [PATCH 60/75] feat(notifiers): Fixes #44 only for async_std --- Cargo.toml | 2 +- examples/async-std/client/src/main.rs | 2 +- examples/async-std/server/Cargo.toml | 2 +- examples/async-std/server/src/main.rs | 2 +- src/client/mod.rs | 495 ++++++++++++++------------ src/connection/mod.rs | 185 +++++----- src/lib.rs | 3 + src/notify/mod.rs | 41 ++- src/protocol/mod.rs | 3 + 9 files changed, 411 insertions(+), 324 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e9b10d..0f08af3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary-utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2", version = "0.2.2" } +binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs index de8dfc7..ebf51cc 100644 --- a/examples/async-std/client/src/main.rs +++ b/examples/async-std/client/src/main.rs @@ -4,7 +4,7 @@ use std::{net::ToSocketAddrs, vec}; #[async_std::main] async fn main() { let mut client = Client::new(10, DEFAULT_MTU); - let mut addr = "ownagepe.com:19132".to_socket_addrs().unwrap(); + let mut addr = "zeqa.net:19132".to_socket_addrs().unwrap(); if let Err(_) = client.connect(addr.next().unwrap()).await { // here you could attempt to retry, but in this case, we'll just exit println!("Failed to connect to server!"); diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml index 93ac061..eb05ae4 100644 --- a/examples/async-std/server/Cargo.toml +++ b/examples/async-std/server/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "attributes" ] } console-subscriber = "0.1.8" -rak_rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false} \ No newline at end of file diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs index c99e558..a8487d6 100644 --- a/examples/async-std/server/src/main.rs +++ b/examples/async-std/server/src/main.rs @@ -20,7 +20,7 @@ async fn main() { async fn handle(mut conn: Connection) { loop { // keeping the connection alive - if conn.is_closed() { + if conn.is_closed().await { println!("Connection closed!"); break; } diff --git a/src/client/mod.rs b/src/client/mod.rs index 07a0480..3e5ebb9 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,23 +4,27 @@ use std::{ net::SocketAddr, sync::{ atomic::{AtomicBool, AtomicU64}, - Arc, + Arc }, task::Waker, time::Duration, }; -#[cfg(feature = "async_tokio")] -use crate::connection::RecvError; -#[cfg(feature = "async_std")] -use async_std::future::timeout; #[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, RecvError, Sender}, net::UdpSocket, sync::{Mutex, RwLock}, task::{self, sleep, JoinHandle}, + future::timeout }; + +#[cfg(feature = "async_std")] +use futures::{ + select, + FutureExt +}; + use binary_utils::Streamable; #[cfg(feature = "async_tokio")] @@ -32,14 +36,20 @@ use tokio::{ }, task::{self, JoinHandle}, time::{sleep, timeout}, + select }; + +#[cfg(feature = "async_tokio")] +use crate::connection::RecvError; + use crate::{ connection::{ queue::{RecvQueue, SendQueue}, state::ConnectionState, }, error::client::ClientError, + notify::Notify, protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, @@ -82,7 +92,7 @@ pub struct Client { /// A list of tasks that are killed when the connection drops. tasks: Arc>>>, /// A notifier for when the client should kill threads. - closed: Arc, + close_notifier: Arc>, /// A int for the last time a packet was received. recv_time: Arc, /// The maximum packet size that can be sent to the server. @@ -106,7 +116,7 @@ impl Client { mtu, version, tasks: Arc::new(Mutex::new(Vec::new())), - closed: Arc::new(AtomicBool::new(false)), + close_notifier: Arc::new(Mutex::new(Notify::new())), recv_time: Arc::new(AtomicU64::new(0)), internal_recv, internal_send, @@ -151,8 +161,8 @@ impl Client { if res.is_err() { rakrs_debug!("[CLIENT] Failed to connect to address"); - self.closed - .store(false, std::sync::atomic::Ordering::Relaxed); + // todo: properly handle lock. + self.close_notifier.lock().await.notify().await; return Err(ClientError::Killed); } @@ -170,7 +180,7 @@ impl Client { self.network_recv = Some(Arc::new(Mutex::new(net_recv))); - let closer = self.closed.clone(); + let closer = self.close_notifier.clone(); Self::ping(socket.clone()).await?; @@ -190,30 +200,35 @@ impl Client { let socket_task = task::spawn(async move { let mut buf: [u8; 2048] = [0; 2048]; + let notifier = closer.lock().await; loop { - if closer.load(std::sync::atomic::Ordering::Relaxed) { - rakrs_debug!(true, "[CLIENT] Network recv task closed"); - break; - } - let length: usize; - let recv = socket.recv(&mut buf).await; - match recv { - Ok(l) => length = l, - Err(e) => { - rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); - continue; + select! { + killed = notifier.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Socket task closed"); + break; + } } - } - // no assertions because this is a client - // this allows the user to customize their own packet handling - // todo: the logic in the recv_task may be better here, as this is latent - if let Err(_) = net_send.send(buf[..length].to_vec()).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); - } + recv = socket.recv(&mut buf).fuse() => { + match recv { + Ok(l) => length = l, + Err(e) => { + rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); + continue; + } + } + // no assertions because this is a client + // this allows the user to customize their own packet handling + // todo: the logic in the recv_task may be better here, as this is latent + if let Err(_) = net_send.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); + } + } + }; } }); @@ -239,8 +254,8 @@ impl Client { /// Todo: send disconnect packet. pub async fn close(&self) { self.update_state(ConnectionState::Disconnecting).await; - self.closed - .store(true, std::sync::atomic::Ordering::Relaxed); + let notifier = self.close_notifier.clone(); + notifier.lock().await.notify().await; let mut tasks = self.tasks.lock().await; for task in tasks.drain(..) { #[cfg(feature = "async_std")] @@ -439,168 +454,175 @@ impl Client { let recv_queue = self.recv_queue.clone(); let internal_sender = self.internal_send.clone(); - let closed = self.closed.clone(); + let closed = self.close_notifier.clone(); let state = self.state.clone(); let recv_time = self.recv_time.clone(); let r = task::spawn(async move { 'task_loop: loop { - let pk_recv = net_recv.lock().await.recv().await; - if closed.load(std::sync::atomic::Ordering::Relaxed) == true { - rakrs_debug!("[CLIENT] (recv_task) The internal network recieve channel has been killed."); - break; - } - - #[cfg(feature = "async_std")] - if let Err(_) = pk_recv { - rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); - continue; - } - - #[cfg(feature = "async_tokio")] - if let None = pk_recv { - rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); - continue; - } - - recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); - - let mut client_state = state.lock().await; - - if *client_state == ConnectionState::TimingOut { - rakrs_debug!(true, "[CLIENT] (recv_task) Client is no longer timing out!"); - *client_state = ConnectionState::Connected; - } - - if *client_state == ConnectionState::Disconnecting { - rakrs_debug!(true, "[CLIENT] (recv_task) Client is disconnecting!"); - break; - } - - // drop here so the lock isn't held for too long - drop(client_state); - - let mut buffer = pk_recv.unwrap(); - - match buffer[0] { - 0x80..=0x8d => { - if let Ok(frame_packet) = FramePacket::compose(&mut buffer[..], &mut 0) { - let mut recv_q = recv_queue.lock().await; - if let Err(_) = recv_q.insert(frame_packet) { - rakrs_debug!( - true, - "[CLIENT] Failed to push frame packet into send queue." - ); - } - - let buffers = recv_q.flush(); + let net_dispatch = net_recv.lock().await; + let closed_dispatch = closed.lock().await; + select! { + killed = closed_dispatch.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Recv task closed"); + break; + } + } + pk_recv = net_dispatch.recv().fuse() => { - 'buf_loop: for mut pk_buf in buffers { - if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { - if packet.is_online() { - match packet.get_online() { - OnlinePacket::ConnectedPing(pk) => { - let response = ConnectedPong { - ping_time: pk.time, - pong_time: current_epoch() as i64, + #[cfg(feature = "async_std")] + if let Err(_) = pk_recv { + rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + #[cfg(feature = "async_tokio")] + if let None = pk_recv { + rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + + let mut client_state = state.lock().await; + + if *client_state == ConnectionState::TimingOut { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is no longer timing out!"); + *client_state = ConnectionState::Connected; + } + + if *client_state == ConnectionState::Disconnecting { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is disconnecting!"); + break; + } + + // drop here so the lock isn't held for too long + drop(client_state); + + let mut buffer = pk_recv.unwrap(); + + match buffer[0] { + 0x80..=0x8d => { + if let Ok(frame_packet) = FramePacket::compose(&mut buffer[..], &mut 0) { + let mut recv_q = recv_queue.lock().await; + if let Err(_) = recv_q.insert(frame_packet) { + rakrs_debug!( + true, + "[CLIENT] Failed to push frame packet into send queue." + ); + } + + let buffers = recv_q.flush(); + + 'buf_loop: for mut pk_buf in buffers { + if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { + if packet.is_online() { + match packet.get_online() { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_queue.write().await; + if let Err(_) = q + .send_packet( + response.into(), + Reliability::Unreliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + continue 'buf_loop; + } + OnlinePacket::ConnectedPong(_) => { + // todo: add ping time to client + rakrs_debug!( + true, + "[CLIENT] Recieved pong packet!" + ); + } + OnlinePacket::Disconnect(_) => { + rakrs_debug!( + true, + "[CLIENT] Recieved disconnect packet!" + ); + break 'task_loop; + } + _ => {} }; - let mut q = send_queue.write().await; - if let Err(_) = q - .send_packet( - response.into(), - Reliability::Unreliable, - true, - ) + + rakrs_debug!( + true, + "[CLIENT] Processing fault packet... {:#?}", + packet + ); + + if let Err(_) = internal_sender.send(pk_buf).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } else { + // we should never recieve an offline packet after the handshake + rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); + } + } else { + // we send this packet + if let Err(_) = internal_sender.send(pk_buf).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } + } + } + NACK => { + if let Ok(nack) = Ack::compose(&mut buffer[..], &mut 0) { + let mut send_q = send_queue.write().await; + let to_resend = send_q.nack(nack); + + if to_resend.len() > 0 { + for ack_packet in to_resend { + if let Ok(buffer) = ack_packet.parse() { + if let Err(_) = send_q + .insert(buffer, Reliability::Unreliable, true, Some(0)) .await { rakrs_debug!( true, - "[CLIENT] Failed to send pong packet!" + "[CLIENT] Failed to insert ack packet into send queue!" ); } - continue 'buf_loop; - } - OnlinePacket::ConnectedPong(_) => { - // todo: add ping time to client - rakrs_debug!( - true, - "[CLIENT] Recieved pong packet!" - ); - } - OnlinePacket::Disconnect(_) => { + } else { rakrs_debug!( true, - "[CLIENT] Recieved disconnect packet!" + "[CLIENT] Failed to send packet to client (parsing failed)!", ); - break 'task_loop; } - _ => {} - }; - - rakrs_debug!( - true, - "[CLIENT] Processing fault packet... {:#?}", - packet - ); - - if let Err(_) = internal_sender.send(pk_buf).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } - } else { - // we should never recieve an offline packet after the handshake - rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); - } - } else { - // we send this packet - if let Err(_) = internal_sender.send(pk_buf).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } } } - } - } - NACK => { - if let Ok(nack) = Ack::compose(&mut buffer[..], &mut 0) { - let mut send_q = send_queue.write().await; - let to_resend = send_q.nack(nack); - - if to_resend.len() > 0 { - for ack_packet in to_resend { - if let Ok(buffer) = ack_packet.parse() { - if let Err(_) = send_q - .insert(buffer, Reliability::Unreliable, true, Some(0)) - .await - { - rakrs_debug!( - true, - "[CLIENT] Failed to insert ack packet into send queue!" - ); - } - } else { - rakrs_debug!( - true, - "[CLIENT] Failed to send packet to client (parsing failed)!", - ); - } + ACK => { + if let Ok(ack) = Ack::compose(&mut buffer[..], &mut 0) { + let mut send_q = send_queue.write().await; + send_q.ack(ack.clone()); + + drop(send_q); + + recv_queue.lock().await.ack(ack); + } + } + _ => { + // we don't know what this is, so we're going to send it to the user, maybe + // this is a custom packet + if let Err(_) = internal_sender.send(buffer).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } } - } - } - ACK => { - if let Ok(ack) = Ack::compose(&mut buffer[..], &mut 0) { - let mut send_q = send_queue.write().await; - send_q.ack(ack.clone()); - - drop(send_q); - - recv_queue.lock().await.ack(ack); - } - } - _ => { - // we don't know what this is, so we're going to send it to the user, maybe - // this is a custom packet - if let Err(_) = internal_sender.send(buffer).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } } } @@ -621,7 +643,7 @@ impl Client { } self.update_state(ConnectionState::Connected).await; - let closer = self.closed.clone(); + let closer_dispatch = self.close_notifier.clone(); let recv_queue = self.recv_queue.clone(); let state = self.state.clone(); let last_recv = self.recv_time.clone(); @@ -629,76 +651,80 @@ impl Client { let t = task::spawn(async move { loop { - sleep(Duration::from_millis(50)).await; - - if closer.load(std::sync::atomic::Ordering::Relaxed) == true { - rakrs_debug!(true, "[CLIENT] Connect tick task closed"); - break; - } - - let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); - let mut state = state.lock().await; + let mut closer = closer_dispatch.lock().await; + select! { + _ = sleep(Duration::from_millis(50)).fuse() => { + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut state = state.lock().await; + + if *state == ConnectionState::Disconnected { + rakrs_debug!( + true, + "[CLIENT] Client is disconnected. Closing connect tick task" + ); + closer.notify().await; + break; + } - if *state == ConnectionState::Disconnected { - rakrs_debug!( - true, - "[CLIENT] Client is disconnected. Closing connect tick task" - ); - closer.store(true, std::sync::atomic::Ordering::Relaxed); - break; - } + if *state == ConnectionState::Connecting { + rakrs_debug!( + true, + "[CLIENT] Client is not fully connected to the server yet." + ); + continue; + } - if *state == ConnectionState::Connecting { - rakrs_debug!( - true, - "[CLIENT] Client is not fully connected to the server yet." - ); - continue; - } + if recv + 20000 <= current_epoch() { + *state = ConnectionState::Disconnected; + rakrs_debug!(true, "[CLIENT] Client timed out. Closing connection..."); + closer.notify().await; + break; + } - if recv + 20000 <= current_epoch() { - *state = ConnectionState::Disconnected; - rakrs_debug!(true, "[CLIENT] Client timed out. Closing connection..."); - closer.store(true, std::sync::atomic::Ordering::Relaxed); - break; - } + if recv + 15000 <= current_epoch() && state.is_reliable() { + *state = ConnectionState::TimingOut; + rakrs_debug!(true, "[CLIENT] Connection is timing out, sending a ping!",); + } - if recv + 15000 <= current_epoch() && state.is_reliable() { - *state = ConnectionState::TimingOut; - rakrs_debug!(true, "[CLIENT] Connection is timing out, sending a ping!",); - } + let mut send_q = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; - let mut send_q = send_queue.write().await; - let mut recv_q = recv_queue.lock().await; - - if last_ping >= 3000 { - let ping = ConnectedPing { - time: current_epoch() as i64, - }; - if let Ok(_) = send_q - .send_packet(ping.into(), Reliability::Reliable, true) - .await - {} - last_ping = 0; - } else { - last_ping += 50; - } + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = send_q + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {} + last_ping = 0; + } else { + last_ping += 50; + } - send_q.update().await; + send_q.update().await; - // Flush the queue of acks and nacks, and respond to them - let ack = Ack::from_records(recv_q.ack_flush(), false); - if ack.records.len() > 0 { - if let Ok(p) = ack.parse() { - send_q.send_stream(&p).await; - } - } + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.parse() { + send_q.send_stream(&p).await; + } + } - // flush nacks from recv queue - let nack = Ack::from_records(recv_q.nack_queue(), true); - if nack.records.len() > 0 { - if let Ok(p) = nack.parse() { - send_q.send_stream(&p).await; + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.parse() { + send_q.send_stream(&p).await; + } + } + }, + killed = closer.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Connect tick task closed"); + break; + } } } } @@ -709,7 +735,8 @@ impl Client { impl Drop for Client { fn drop(&mut self) { - self.closed - .store(true, std::sync::atomic::Ordering::Relaxed); + futures_executor::block_on(async move { + self.close_notifier.lock().await.notify().await + }); } } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 06afd4a..323e4bd 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -12,6 +12,8 @@ use std::{ time::Duration, }; +use binary_utils::Streamable; + #[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, RecvError, Sender}, @@ -19,7 +21,11 @@ use async_std::{ sync::{Mutex, RwLock}, task::{self, sleep, JoinHandle}, }; -use binary_utils::Streamable; +#[cfg(feature = "async_std")] +use futures::{ + select, + FutureExt +}; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -48,7 +54,7 @@ use crate::{ }, rakrs_debug, server::current_epoch, - util::to_address_token, + util::to_address_token, notify::Notify, }; use self::{ @@ -98,7 +104,7 @@ pub struct Connection { internal_net_recv: ConnNetChan, /// A notifier for when the connection should close. /// This is used for absolute cleanup withtin the connection - disconnect: Arc, + disconnect: Arc, /// The event dispatcher for the connection. // evt_sender: Sender<(ServerEvent, oneshot::Sender)>, /// The event receiver for the connection. @@ -134,7 +140,7 @@ impl Connection { // evt_receiver, state: Arc::new(Mutex::new(ConnectionState::Unidentified)), // disconnect: Arc::new(Condvar::new()), - disconnect: Arc::new(AtomicBool::new(false)), + disconnect: Arc::new(Notify::new()), recv_time: Arc::new(AtomicU64::new(current_epoch())), tasks: Arc::new(Mutex::new(Vec::new())), }; @@ -162,76 +168,89 @@ impl Connection { // while handling throttle return task::spawn(async move { loop { - sleep(Duration::from_millis(50)).await; - let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); - let mut cstate = state.lock().await; - - if *cstate == ConnectionState::Disconnected { - rakrs_debug!( - true, - "[{}] Connection has been closed due to state!", - to_address_token(address) - ); - // closer.notify_all(); - closer.store(true, std::sync::atomic::Ordering::Relaxed); - break; - } + select! { + _ = closer.wait().fuse() => { + rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); + break; + } + _ = sleep(Duration::from_millis(50)).fuse() => { + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut cstate = state.lock().await; - if recv + 20000 <= current_epoch() { - *cstate = ConnectionState::Disconnected; - rakrs_debug!( - true, - "[{}] Connection has been closed due to inactivity!", - to_address_token(address) - ); - // closer.notify_all(); - closer.store(true, std::sync::atomic::Ordering::Relaxed); - break; - } + if *cstate == ConnectionState::Disconnected { + rakrs_debug!( + true, + "[{}] Connection has been closed due to state!", + to_address_token(address) + ); + // closer.notify_all(); + closer.notify().await; + break; + } - if recv + 15000 <= current_epoch() && cstate.is_reliable() { - *cstate = ConnectionState::TimingOut; - rakrs_debug!( - true, - "[{}] Connection is timing out, sending a ping!", - to_address_token(address) - ); - } + if recv + 20000 <= current_epoch() { + *cstate = ConnectionState::Disconnected; + rakrs_debug!( + true, + "[{}] Connection has been closed due to inactivity!", + to_address_token(address) + ); + // closer.notify_all(); + closer.notify().await; + break; + } - let mut sendq = send_queue.write().await; - let mut recv_q = recv_queue.lock().await; + if recv + 15000 <= current_epoch() && cstate.is_reliable() { + *cstate = ConnectionState::TimingOut; + rakrs_debug!( + true, + "[{}] Connection is timing out, sending a ping!", + to_address_token(address) + ); + } - if last_ping >= 3000 { - let ping = ConnectedPing { - time: current_epoch() as i64, - }; - if let Ok(_) = sendq - .send_packet(ping.into(), Reliability::Reliable, true) - .await - {}; - last_ping = 0; - } else { - last_ping += 50; - } + let mut sendq = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; + + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = sendq + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {}; + last_ping = 0; + } else { + last_ping += 50; + } - sendq.update().await; + sendq.update().await; - // Flush the queue of acks and nacks, and respond to them - let ack = Ack::from_records(recv_q.ack_flush(), false); - if ack.records.len() > 0 { - if let Ok(p) = ack.parse() { - sendq.send_stream(&p).await; - } - } + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.parse() { + sendq.send_stream(&p).await; + } + } - // flush nacks from recv queue - let nack = Ack::from_records(recv_q.nack_queue(), true); - if nack.records.len() > 0 { - if let Ok(p) = nack.parse() { - sendq.send_stream(&p).await; + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.parse() { + sendq.send_stream(&p).await; + } + } } } } + + rakrs_debug!( + true, + "[{}] Connection has been closed due to end of tick!", + to_address_token(address) + ); }); } @@ -251,14 +270,6 @@ impl Connection { return task::spawn(async move { loop { - if disconnect.load(std::sync::atomic::Ordering::Relaxed) { - rakrs_debug!( - true, - "[{}] Recv task has been closed!", - to_address_token(address) - ); - break; - } macro_rules! handle_payload { ($payload: ident) => { // We've recieved a payload! @@ -297,7 +308,7 @@ impl Connection { // DISCONNECT // disconnect.close(); rakrs_debug!(true, "[{}] Connection::process_packet returned true!", to_address_token(address)); - disconnect.store(true, std::sync::atomic::Ordering::Relaxed); + disconnect.notify().await; break; } } @@ -363,16 +374,24 @@ impl Connection { }; } - match net.recv().await { - #[cfg(feature = "async_std")] - Ok(payload) => { - handle_payload!(payload); + select! { + _ = disconnect.wait().fuse() => { + rakrs_debug!(true, "[{}] [RECV TASK] Connection has been closed due to closer!", to_address_token(address)); + break; } - #[cfg(feature = "async_tokio")] - Some(payload) => { - handle_payload!(payload); + res = net.recv().fuse() => { + match res { + #[cfg(feature = "async_std")] + Ok(payload) => { + handle_payload!(payload); + } + #[cfg(feature = "async_tokio")] + Some(payload) => { + handle_payload!(payload); + } + _ => continue, + } } - _ => continue, } } }); @@ -521,8 +540,8 @@ impl Connection { // } // } - pub fn is_closed(&self) -> bool { - self.disconnect.load(std::sync::atomic::Ordering::Acquire) + pub async fn is_closed(&self) -> bool { + !self.state.lock().await.is_available() } /// Send a packet to the client. diff --git a/src/lib.rs b/src/lib.rs index b974eb3..c7c03ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,3 +116,6 @@ pub mod util; pub use protocol::mcpe::{self, motd::Motd}; pub use server::Listener; + +/// An internal module for notifying the connection of state updates. +pub(crate) mod notify; diff --git a/src/notify/mod.rs b/src/notify/mod.rs index 332b41b..5c92347 100644 --- a/src/notify/mod.rs +++ b/src/notify/mod.rs @@ -1,4 +1,39 @@ -/// Notifies any task to wake up -pub struct Notify { +#[cfg(feature = "async_std")] +use async_std::channel::{Receiver, Sender}; -} \ No newline at end of file +#[cfg(feature = "async_tokio")] +use tokio::sync::mpsc::{Receiver, Sender}; + +use crate::connection::queue::recv; + +/// Notify is a struct that wraps a buffer channel +/// these channels are used to send messages to the main thread. +#[derive(Clone)] +pub struct Notify(pub Option>, pub Receiver<()>); + +impl Notify { + /// Creates a new Notify struct. + pub fn new() -> Self { + let (send, recv) = async_std::channel::bounded(1); + Self(Some(send), recv) + } + + /// Sends a message to all listeners. + pub async fn notify(&self) -> bool { + if let Some(sender) = &self.0 { + sender.close(); + true + } else { + false + } + } + + /// Waits for a message from the main thread. + pub async fn wait(&self) -> bool { + if let Err(_) = self.1.recv().await { + false + } else { + true + } + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index af955c9..1c324d4 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -14,3 +14,6 @@ pub const MAX_ORD_CHANS: u8 = 32; pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20; // IP Header + UDP Header + RakNet Header pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; + +pub const MTU_MAX: u16 = 2400; +pub const MTU_MIN: u16 = 400; From 5d5be753eb0b3c30b8025a42064a8ea7718c86ef Mon Sep 17 00:00:00 2001 From: john-bv Date: Tue, 8 Aug 2023 22:34:46 -0500 Subject: [PATCH 61/75] chore(fmt): Format files using cargo --- src/client/mod.rs | 46 +++++++++++++++++++------------------------ src/connection/mod.rs | 8 +++----- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 3e5ebb9..0681f98 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,7 +4,7 @@ use std::{ net::SocketAddr, sync::{ atomic::{AtomicBool, AtomicU64}, - Arc + Arc, }, task::Waker, time::Duration, @@ -13,33 +13,29 @@ use std::{ #[cfg(feature = "async_std")] use async_std::{ channel::{bounded, Receiver, RecvError, Sender}, + future::timeout, net::UdpSocket, sync::{Mutex, RwLock}, task::{self, sleep, JoinHandle}, - future::timeout }; #[cfg(feature = "async_std")] -use futures::{ - select, - FutureExt -}; +use futures::{select, FutureExt}; use binary_utils::Streamable; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, + select, sync::{ mpsc::{channel as bounded, Receiver, Sender}, Mutex, RwLock, }, task::{self, JoinHandle}, time::{sleep, timeout}, - select }; - #[cfg(feature = "async_tokio")] use crate::connection::RecvError; @@ -476,32 +472,32 @@ impl Client { rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); continue; } - + #[cfg(feature = "async_tokio")] if let None = pk_recv { rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); continue; } - + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); - + let mut client_state = state.lock().await; - + if *client_state == ConnectionState::TimingOut { rakrs_debug!(true, "[CLIENT] (recv_task) Client is no longer timing out!"); *client_state = ConnectionState::Connected; } - + if *client_state == ConnectionState::Disconnecting { rakrs_debug!(true, "[CLIENT] (recv_task) Client is disconnecting!"); break; } - + // drop here so the lock isn't held for too long drop(client_state); - + let mut buffer = pk_recv.unwrap(); - + match buffer[0] { 0x80..=0x8d => { if let Ok(frame_packet) = FramePacket::compose(&mut buffer[..], &mut 0) { @@ -512,9 +508,9 @@ impl Client { "[CLIENT] Failed to push frame packet into send queue." ); } - + let buffers = recv_q.flush(); - + 'buf_loop: for mut pk_buf in buffers { if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { if packet.is_online() { @@ -556,13 +552,13 @@ impl Client { } _ => {} }; - + rakrs_debug!( true, "[CLIENT] Processing fault packet... {:#?}", packet ); - + if let Err(_) = internal_sender.send(pk_buf).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } @@ -583,7 +579,7 @@ impl Client { if let Ok(nack) = Ack::compose(&mut buffer[..], &mut 0) { let mut send_q = send_queue.write().await; let to_resend = send_q.nack(nack); - + if to_resend.len() > 0 { for ack_packet in to_resend { if let Ok(buffer) = ack_packet.parse() { @@ -610,9 +606,9 @@ impl Client { if let Ok(ack) = Ack::compose(&mut buffer[..], &mut 0) { let mut send_q = send_queue.write().await; send_q.ack(ack.clone()); - + drop(send_q); - + recv_queue.lock().await.ack(ack); } } @@ -735,8 +731,6 @@ impl Client { impl Drop for Client { fn drop(&mut self) { - futures_executor::block_on(async move { - self.close_notifier.lock().await.notify().await - }); + futures_executor::block_on(async move { self.close_notifier.lock().await.notify().await }); } } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 323e4bd..a047bea 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -22,10 +22,7 @@ use async_std::{ task::{self, sleep, JoinHandle}, }; #[cfg(feature = "async_std")] -use futures::{ - select, - FutureExt -}; +use futures::{select, FutureExt}; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -43,6 +40,7 @@ pub enum RecvError { } use crate::{ + notify::Notify, protocol::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, @@ -54,7 +52,7 @@ use crate::{ }, rakrs_debug, server::current_epoch, - util::to_address_token, notify::Notify, + util::to_address_token, }; use self::{ From 84766506c67f039642b8b8daa9eed1d14e5954c3 Mon Sep 17 00:00:00 2001 From: john-bv Date: Tue, 8 Aug 2023 22:37:26 -0500 Subject: [PATCH 62/75] chore(fmt): Remove warnings [ci skip] --- src/client/mod.rs | 8 ++------ src/connection/mod.rs | 5 +---- src/notify/mod.rs | 2 -- src/protocol/ack.rs | 2 -- src/protocol/packet/offline.rs | 1 - 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 0681f98..f567d17 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -2,11 +2,7 @@ pub mod handshake; use std::{ net::SocketAddr, - sync::{ - atomic::{AtomicBool, AtomicU64}, - Arc, - }, - task::Waker, + sync::{atomic::AtomicU64, Arc}, time::Duration, }; @@ -647,7 +643,7 @@ impl Client { let t = task::spawn(async move { loop { - let mut closer = closer_dispatch.lock().await; + let closer = closer_dispatch.lock().await; select! { _ = sleep(Duration::from_millis(50)).fuse() => { let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); diff --git a/src/connection/mod.rs b/src/connection/mod.rs index a047bea..f31fe5d 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -5,10 +5,7 @@ pub mod state; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{ - atomic::{AtomicBool, AtomicU64}, - Arc, - }, + sync::{atomic::AtomicU64, Arc}, time::Duration, }; diff --git a/src/notify/mod.rs b/src/notify/mod.rs index 5c92347..b798e20 100644 --- a/src/notify/mod.rs +++ b/src/notify/mod.rs @@ -4,8 +4,6 @@ use async_std::channel::{Receiver, Sender}; #[cfg(feature = "async_tokio")] use tokio::sync::mpsc::{Receiver, Sender}; -use crate::connection::queue::recv; - /// Notify is a struct that wraps a buffer channel /// these channels are used to send messages to the main thread. #[derive(Clone)] diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index dd69f26..412eabb 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -6,8 +6,6 @@ use std::{io::Cursor, ops::Range}; use binary_utils::Streamable; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; -use crate::rakrs_debug; - pub(crate) trait Ackable { type NackItem; diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 1f07d61..fbc694c 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -7,7 +7,6 @@ use byteorder::WriteBytesExt; #[cfg(feature = "mcpe")] pub use crate::protocol::mcpe::UnconnectedPong; -use crate::protocol::MAGIC; use crate::protocol::RAKNET_HEADER_OVERHEAD; use super::Packet; From d7c856aa5984c1de5183bca262f8acbc4dea1a14 Mon Sep 17 00:00:00 2001 From: john-bv Date: Wed, 9 Aug 2023 20:10:46 -0500 Subject: [PATCH 63/75] feat(src/notify): Add notifiers for tokio & cleanup conditional imports --- Cargo.toml | 3 +- examples/async-std/server/Cargo.toml | 2 +- examples/async-std/server/src/main.rs | 2 +- examples/tokio/server/Cargo.toml | 2 +- examples/tokio/server/src/main.rs | 8 +- src/client/handshake.rs | 2 +- src/client/mod.rs | 109 ++++++++++++++++++++++---- src/connection/mod.rs | 54 ++++++++++--- src/notify/mod.rs | 40 ++-------- 9 files changed, 153 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0f08af3..83e002c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ authors = ["Bavfalcon9 "] edition = "2021" [features] -default = [ "async_std" ] +# default = [ "async_std" ] +default = ["async_tokio" ] mcpe = [] debug = [] debug_all = [] diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml index eb05ae4..949c775 100644 --- a/examples/async-std/server/Cargo.toml +++ b/examples/async-std/server/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "attributes" ] } console-subscriber = "0.1.8" -rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false} \ No newline at end of file +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs index a8487d6..34b2d00 100644 --- a/examples/async-std/server/src/main.rs +++ b/examples/async-std/server/src/main.rs @@ -6,7 +6,7 @@ use rak_rs::Listener; async fn main() { // console_subscriber::init(); let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); - server.motd.name = "RakNet Rust!".to_string(); + server.motd.name = "RakNet Rust (async-std)!".to_string(); server.motd.gamemode = Gamemode::Survival; server.start().await.unwrap(); diff --git a/examples/tokio/server/Cargo.toml b/examples/tokio/server/Cargo.toml index ebea5d4..e41859a 100644 --- a/examples/tokio/server/Cargo.toml +++ b/examples/tokio/server/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "attributes" ] } console-subscriber = "0.1.8" -rak_rs = { path = "../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } tokio = "1.23.0" diff --git a/examples/tokio/server/src/main.rs b/examples/tokio/server/src/main.rs index 7d81efd..67c3870 100644 --- a/examples/tokio/server/src/main.rs +++ b/examples/tokio/server/src/main.rs @@ -11,8 +11,7 @@ use rak_rs::server::event::ServerEventResponse; async fn main() { console_subscriber::init(); let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); - // let inner = server.recv_evnt.clone(); - server.motd.name = "RakNet Rust!".to_string(); + server.motd.name = "RakNet Rust (tokio)!".to_string(); server.motd.gamemode = Gamemode::Survival; server.start().await.unwrap(); @@ -26,13 +25,12 @@ async fn main() { async fn handle(mut conn: Connection) { loop { // keeping the connection alive - if conn.is_closed() { + if conn.is_closed().await { println!("Connection closed!"); break; } if let Ok(pk) = conn.recv().await { - println!("Got a connection packet {:?} ", pk); + println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); } - // conn.tick().await; } } \ No newline at end of file diff --git a/src/client/handshake.rs b/src/client/handshake.rs index a4c5c5f..30463b6 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -19,7 +19,7 @@ use std::task::{Context, Poll, Waker}; use tokio::{ net::UdpSocket, task::{self}, - time::{sleep, timeout}, + time::timeout, }; use crate::connection::queue::send::SendQueue; diff --git a/src/client/mod.rs b/src/client/mod.rs index f567d17..ce3a5a1 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -197,6 +197,7 @@ impl Client { loop { let length: usize; + #[cfg(feature = "async_std")] select! { killed = notifier.wait().fuse() => { if killed { @@ -221,6 +222,31 @@ impl Client { } } }; + + #[cfg(feature = "async_tokio")] + select! { + killed = notifier.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Socket task closed"); + break; + } + } + + recv = socket.recv(&mut buf) => { + match recv { + Ok(l) => length = l, + Err(e) => { + rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); + continue; + } + } + // no assertions because this is a client + // this allows the user to customize their own packet handling + if let Err(_) = net_send.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); + } + } + }; } }); @@ -366,15 +392,20 @@ impl Client { } } + #[cfg(feature = "async_std")] pub async fn recv(&self) -> Result, RecvError> { match self.internal_recv.recv().await { #[cfg(feature = "async_std")] Ok(packet) => Ok(packet), #[cfg(feature = "async_std")] - Err(e) => Err(e), - #[cfg(feature = "async_tokio")] + Err(e) => Err(e) + } + } + + #[cfg(feature = "async_tokio")] + pub async fn recv(&mut self) -> Result, RecvError> { + match self.internal_recv.recv().await { Some(packet) => Ok(packet), - #[cfg(feature = "async_tokio")] None => Err(RecvError::Closed), } } @@ -452,25 +483,22 @@ impl Client { let r = task::spawn(async move { 'task_loop: loop { + #[cfg(feature = "async_std")] let net_dispatch = net_recv.lock().await; - let closed_dispatch = closed.lock().await; - select! { - killed = closed_dispatch.wait().fuse() => { - if killed { - rakrs_debug!(true, "[CLIENT] Recv task closed"); - break; - } - } - pk_recv = net_dispatch.recv().fuse() => { + #[cfg(feature = "async_tokio")] + let mut net_dispatch = net_recv.lock().await; + let closed_dispatch = closed.lock().await; + macro_rules! recv_body { + ($pk_recv: expr) => { #[cfg(feature = "async_std")] - if let Err(_) = pk_recv { + if let Err(_) = $pk_recv { rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); continue; } #[cfg(feature = "async_tokio")] - if let None = pk_recv { + if let None = $pk_recv { rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); continue; } @@ -492,7 +520,7 @@ impl Client { // drop here so the lock isn't held for too long drop(client_state); - let mut buffer = pk_recv.unwrap(); + let mut buffer = $pk_recv.unwrap(); match buffer[0] { 0x80..=0x8d => { @@ -616,6 +644,32 @@ impl Client { } } } + }; + } + + #[cfg(feature = "async_std")] + select! { + killed = closed_dispatch.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Recv task closed"); + break; + } + } + pk_recv = net_dispatch.recv().fuse() => { + recv_body!(pk_recv); + } + } + + #[cfg(feature = "async_tokio")] + select! { + killed = closed_dispatch.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Recv task closed"); + break; + } + } + pk_recv = net_dispatch.recv() => { + recv_body!(pk_recv); } } } @@ -644,8 +698,9 @@ impl Client { let t = task::spawn(async move { loop { let closer = closer_dispatch.lock().await; - select! { - _ = sleep(Duration::from_millis(50)).fuse() => { + + macro_rules! tick_body { + () => { let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); let mut state = state.lock().await; @@ -711,6 +766,13 @@ impl Client { send_q.send_stream(&p).await; } } + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = sleep(Duration::from_millis(50)).fuse() => { + tick_body!(); }, killed = closer.wait().fuse() => { if killed { @@ -719,6 +781,19 @@ impl Client { } } } + + #[cfg(feature = "async_tokio")] + select! { + _ = sleep(Duration::from_millis(50)) => { + tick_body!(); + }, + killed = closer.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Connect tick task closed"); + break; + } + } + } } }); Ok(t) diff --git a/src/connection/mod.rs b/src/connection/mod.rs index f31fe5d..41260bb 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -27,6 +27,7 @@ use tokio::{ mpsc::{channel as bounded, Receiver, Sender}, Mutex, RwLock, }, + select, task::{self, JoinHandle}, time::sleep, }; @@ -163,12 +164,8 @@ impl Connection { // while handling throttle return task::spawn(async move { loop { - select! { - _ = closer.wait().fuse() => { - rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); - break; - } - _ = sleep(Duration::from_millis(50)).fuse() => { + macro_rules! tick_body { + () => { let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); let mut cstate = state.lock().await; @@ -237,6 +234,28 @@ impl Connection { sendq.send_stream(&p).await; } } + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = closer.wait().fuse() => { + rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); + break; + } + _ = sleep(Duration::from_millis(50)).fuse() => { + tick_body!(); + } + } + + #[cfg(feature = "async_tokio")] + select! { + _ = closer.wait() => { + rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); + break; + } + _ = sleep(Duration::from_millis(50)) => { + tick_body!(); } } } @@ -253,7 +272,12 @@ impl Connection { /// pub async fn init_net_recv( &self, + // THIS IS ONLY ACTIVATED ON STD + #[cfg(feature = "async_std")] net: Receiver>, + // ONLY ACTIVATED ON TOKIO + #[cfg(feature = "async_tokio")] + mut net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); @@ -369,6 +393,7 @@ impl Connection { }; } + #[cfg(feature = "async_std")] select! { _ = disconnect.wait().fuse() => { rakrs_debug!(true, "[{}] [RECV TASK] Connection has been closed due to closer!", to_address_token(address)); @@ -376,18 +401,29 @@ impl Connection { } res = net.recv().fuse() => { match res { - #[cfg(feature = "async_std")] Ok(payload) => { handle_payload!(payload); } - #[cfg(feature = "async_tokio")] + _ => continue, + } + } + }; + + #[cfg(feature = "async_tokio")] + select! { + _ = disconnect.wait() => { + rakrs_debug!(true, "[{}] [RECV TASK] Connection has been closed due to closer!", to_address_token(address)); + break; + } + res = net.recv() => { + match res { Some(payload) => { handle_payload!(payload); } _ => continue, } } - } + }; } }); } diff --git a/src/notify/mod.rs b/src/notify/mod.rs index b798e20..d1c0da9 100644 --- a/src/notify/mod.rs +++ b/src/notify/mod.rs @@ -1,37 +1,11 @@ #[cfg(feature = "async_std")] -use async_std::channel::{Receiver, Sender}; +mod async_std; -#[cfg(feature = "async_tokio")] -use tokio::sync::mpsc::{Receiver, Sender}; +#[cfg(feature = "tokio")] +mod tokio; -/// Notify is a struct that wraps a buffer channel -/// these channels are used to send messages to the main thread. -#[derive(Clone)] -pub struct Notify(pub Option>, pub Receiver<()>); - -impl Notify { - /// Creates a new Notify struct. - pub fn new() -> Self { - let (send, recv) = async_std::channel::bounded(1); - Self(Some(send), recv) - } - - /// Sends a message to all listeners. - pub async fn notify(&self) -> bool { - if let Some(sender) = &self.0 { - sender.close(); - true - } else { - false - } - } +#[cfg(feature = "async_std")] +pub use async_std::Notify; - /// Waits for a message from the main thread. - pub async fn wait(&self) -> bool { - if let Err(_) = self.1.recv().await { - false - } else { - true - } - } -} +#[cfg(feature = "tokio")] +pub use tokio::Notify; \ No newline at end of file From c1b9a41ea273011361c6087bd2c0b6f6f61ee114 Mon Sep 17 00:00:00 2001 From: john-bv Date: Wed, 9 Aug 2023 20:11:07 -0500 Subject: [PATCH 64/75] chore(fmt): Run cargo format --- src/client/mod.rs | 7 +++++-- src/connection/mod.rs | 8 +++----- src/notify/mod.rs | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index ce3a5a1..0294136 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -398,7 +398,7 @@ impl Client { #[cfg(feature = "async_std")] Ok(packet) => Ok(packet), #[cfg(feature = "async_std")] - Err(e) => Err(e) + Err(e) => Err(e), } } @@ -730,7 +730,10 @@ impl Client { if recv + 15000 <= current_epoch() && state.is_reliable() { *state = ConnectionState::TimingOut; - rakrs_debug!(true, "[CLIENT] Connection is timing out, sending a ping!",); + rakrs_debug!( + true, + "[CLIENT] Connection is timing out, sending a ping!", + ); } let mut send_q = send_queue.write().await; diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 41260bb..08a8913 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -23,11 +23,11 @@ use futures::{select, FutureExt}; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, + select, sync::{ mpsc::{channel as bounded, Receiver, Sender}, Mutex, RwLock, }, - select, task::{self, JoinHandle}, time::sleep, }; @@ -273,11 +273,9 @@ impl Connection { pub async fn init_net_recv( &self, // THIS IS ONLY ACTIVATED ON STD - #[cfg(feature = "async_std")] - net: Receiver>, + #[cfg(feature = "async_std")] net: Receiver>, // ONLY ACTIVATED ON TOKIO - #[cfg(feature = "async_tokio")] - mut net: Receiver>, + #[cfg(feature = "async_tokio")] mut net: Receiver>, sender: Sender>, ) -> task::JoinHandle<()> { let recv_time = self.recv_time.clone(); diff --git a/src/notify/mod.rs b/src/notify/mod.rs index d1c0da9..5dcf9df 100644 --- a/src/notify/mod.rs +++ b/src/notify/mod.rs @@ -8,4 +8,4 @@ mod tokio; pub use async_std::Notify; #[cfg(feature = "tokio")] -pub use tokio::Notify; \ No newline at end of file +pub use tokio::Notify; From 155d91700b6fcce578b48357ee86b7047b5e6c1a Mon Sep 17 00:00:00 2001 From: john-bv Date: Wed, 9 Aug 2023 20:14:21 -0500 Subject: [PATCH 65/75] feat(notifiers): Actuall add the files... --- src/notify/async_std.rs | 33 +++++++++++++++++++++++++++++++++ src/notify/tokio.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/notify/async_std.rs create mode 100644 src/notify/tokio.rs diff --git a/src/notify/async_std.rs b/src/notify/async_std.rs new file mode 100644 index 0000000..0e9d2d9 --- /dev/null +++ b/src/notify/async_std.rs @@ -0,0 +1,33 @@ +use async_std::channel::{Receiver, Sender}; + +/// Notify is a struct that wraps a buffer channel +/// these channels are used to send messages to the main thread. +#[derive(Clone)] +pub struct Notify(pub Option>, pub Receiver<()>); + +impl Notify { + /// Creates a new Notify struct. + pub fn new() -> Self { + let (send, recv) = async_std::channel::bounded(1); + Self(Some(send), recv) + } + + /// Sends a message to all listeners. + pub async fn notify(&self) -> bool { + if let Some(sender) = &self.0 { + sender.close(); + true + } else { + false + } + } + + /// Waits for a message from the main thread. + pub async fn wait(&self) -> bool { + if let Err(_) = self.1.recv().await { + false + } else { + true + } + } +} diff --git a/src/notify/tokio.rs b/src/notify/tokio.rs new file mode 100644 index 0000000..0032885 --- /dev/null +++ b/src/notify/tokio.rs @@ -0,0 +1,35 @@ +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::RwLock; + +/// Notify is a struct that wraps a buffer channel +/// these channels are used to send messages to the main thread. +pub struct Notify(RwLock>>, RwLock>>); + +impl Notify { + /// Creates a new Notify struct. + pub fn new() -> Self { + let (send, recv) = tokio::sync::mpsc::channel(1); + Self(RwLock::new(Some(send)), RwLock::new(Some(recv))) + } + + /// Sends a message to all listeners. + pub async fn notify(&self) -> bool { + let mut sender = self.0.write().await; + if let Some(_) = sender.take() { + true + } else { + false + } + } + + /// Waits for a message from the main thread. + pub async fn wait(&self) -> bool { + let mut receiver = self.1.write().await; + if let Some(receiver) = receiver.as_mut() { + receiver.recv().await; + true + } else { + false + } + } +} From 4f7d9490618e7e466409696bbdb50aab97920a93 Mon Sep 17 00:00:00 2001 From: john-bv Date: Wed, 9 Aug 2023 21:37:52 -0500 Subject: [PATCH 66/75] chore: Modify protocol and update to bin-util v3 (pt 1) --- Cargo.toml | 2 +- src/client/handshake.rs | 2 +- src/client/mod.rs | 2 +- src/connection/mod.rs | 172 ++++++++-------- src/connection/queue/send.rs | 6 +- src/protocol/ack.rs | 71 +------ src/protocol/frame.rs | 23 +-- src/protocol/magic.rs | 42 ++-- src/protocol/mcpe/mod.rs | 9 +- src/protocol/mcpe/motd.rs | 171 ++++++++-------- src/protocol/packet/mod.rs | 346 +++------------------------------ src/protocol/packet/offline.rs | 98 ++++------ src/protocol/packet/online.rs | 188 ++++++++++-------- src/server/mod.rs | 345 ++++++++++++++++---------------- 14 files changed, 557 insertions(+), 920 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 83e002c..787dfd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } +binary-util = "0.3.0" tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 30463b6..72832dc 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -10,7 +10,7 @@ use async_std::{ task::{self, Context, Poll, Waker}, }; -use binary_utils::Streamable; +use binary_util::Streamable; #[cfg(feature = "async_tokio")] use std::future::Future; #[cfg(feature = "async_tokio")] diff --git a/src/client/mod.rs b/src/client/mod.rs index 0294136..35203d3 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -18,7 +18,7 @@ use async_std::{ #[cfg(feature = "async_std")] use futures::{select, FutureExt}; -use binary_utils::Streamable; +use binary_util::Streamable; #[cfg(feature = "async_tokio")] use tokio::{ diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 08a8913..c79fd6c 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -9,7 +9,8 @@ use std::{ time::Duration, }; -use binary_utils::Streamable; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::ByteReader; #[cfg(feature = "async_std")] use async_std::{ @@ -43,8 +44,8 @@ use crate::{ ack::{Ack, Ackable, ACK, NACK}, frame::FramePacket, packet::{ + offline::OfflinePacket, online::{ConnectedPing, ConnectedPong, ConnectionAccept, OnlinePacket}, - Packet, }, reliability::Reliability, }, @@ -317,7 +318,7 @@ impl Connection { for buffer in buffers { let res = Connection::process_packet( - &buffer, &address, &sender, &send_q, &state, + ByteReader::from(&buffer), &address, &sender, &send_q, &state, ) .await; if let Ok(v) = res { @@ -427,99 +428,100 @@ impl Connection { } pub async fn process_packet( - buffer: &Vec, + buffer: ByteReader, address: &SocketAddr, sender: &Sender>, send_q: &Arc>, state: &Arc>, ) -> Result { - if let Ok(packet) = Packet::compose(buffer, &mut 0) { - if packet.is_online() { - match packet.get_online() { - OnlinePacket::ConnectedPing(pk) => { - let response = ConnectedPong { - ping_time: pk.time, - pong_time: current_epoch() as i64, - }; - let mut q = send_q.write().await; - if let Ok(_) = q - .send_packet(response.into(), Reliability::Reliable, true) - .await - { - return Ok(false); - } else { - rakrs_debug!( - true, - "[{}] Failed to send ConnectedPong packet!", - to_address_token(*address) - ); - return Err(()); - } - } - OnlinePacket::ConnectedPong(_pk) => { - // do nothing rn - // TODO: add ping calculation + if let Ok(online_packet) = OnlinePacket::read(&mut buffer) { + match online_packet { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_q.write().await; + if let Ok(_) = q + .send_packet(response.into(), Reliability::Reliable, true) + .await + { return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectedPong packet!", + to_address_token(*address) + ); + return Err(()); } - OnlinePacket::ConnectionRequest(pk) => { - let response = ConnectionAccept { - system_index: 0, - client_address: *address, - internal_id: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), - 19132, - ), - request_time: pk.time, - timestamp: current_epoch() as i64, - }; - let mut q = send_q.write().await; - *state.lock().await = ConnectionState::Connecting; - if let Ok(_) = q - .send_packet(response.into(), Reliability::Reliable, true) - .await - { - return Ok(false); - } else { - rakrs_debug!( - true, - "[{}] Failed to send ConnectionAccept packet!", - to_address_token(*address) - ); - return Err(()); - } - } - OnlinePacket::Disconnect(_) => { - // Disconnect the client immediately. - // connection.disconnect("Client disconnected.", false); - return Ok(true); - } - OnlinePacket::LostConnection(_) => { - // Disconnect the client immediately. - // connection.disconnect("Client disconnected.", false); - return Ok(true); - } - OnlinePacket::NewConnection(_) => { - *state.lock().await = ConnectionState::Connected; + } + OnlinePacket::ConnectedPong(_pk) => { + // do nothing rn + // TODO: add ping calculation + return Ok(false); + } + OnlinePacket::ConnectionRequest(pk) => { + let internal_ids = vec![ + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19132), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19133), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19134), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19135), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19136), + ]; + let response = ConnectionAccept { + system_index: 0, + client_address: *address, + internal_ids, + request_time: pk.time, + timestamp: current_epoch() as i64, + }; + let mut q = send_q.write().await; + *state.lock().await = ConnectionState::Connecting; + if let Ok(_) = q + .send_packet(response.into(), Reliability::Reliable, true) + .await + { return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectionAccept packet!", + to_address_token(*address) + ); + return Err(()); } - _ => {} - }; - - sender.send(buffer.clone()).await.unwrap(); - return Ok(false); - } else { - *state.lock().await = ConnectionState::Disconnecting; - rakrs_debug!( - true, - "[{}] Invalid protocol! Disconnecting client!", - to_address_token(*address) - ); - return Err(()); + } + OnlinePacket::Disconnect(_) => { + // Disconnect the client immediately. + // connection.disconnect("Client disconnected.", false); + return Ok(true); + } + OnlinePacket::LostConnection(_) => { + // Disconnect the client immediately. + // connection.disconnect("Client disconnected.", false); + return Ok(true); + } + OnlinePacket::NewConnection(_) => { + *state.lock().await = ConnectionState::Connected; + return Ok(false); + } + _ => {} } + sender.send(buffer.as_slice()).await.unwrap(); + return Ok(false); + } else if let Ok(_) = OfflinePacket::read(&mut buffer) { + sender.send(buffer.as_slice()).await.unwrap(); + return Ok(false); + } else { + *state.lock().await = ConnectionState::Disconnecting; + rakrs_debug!( + true, + "[{}] Invalid protocol! Disconnecting client!", + to_address_token(*address) + ); + return Err(()); } - - sender.send(buffer.clone()).await.unwrap(); - return Ok(false); } /// Recieve a packet from the client. diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 5d689f4..56a4765 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -4,13 +4,13 @@ use std::sync::Arc; #[cfg(feature = "async_std")] use async_std::net::UdpSocket; -use binary_utils::Streamable; +use binary_util::Streamable; #[cfg(feature = "async_tokio")] use tokio::net::UdpSocket; use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; use crate::protocol::frame::{Frame, FramePacket}; -use crate::protocol::packet::Packet; +use crate::protocol::packet::RakPacket; use crate::protocol::reliability::Reliability; use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; use crate::rakrs_debug; @@ -244,7 +244,7 @@ impl SendQueue { pub async fn send_packet( &mut self, - packet: Packet, + packet: RakPacket, reliability: Reliability, immediate: bool, ) -> Result<(), SendQueueError> { diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 412eabb..5f75b27 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -1,10 +1,9 @@ pub const ACK: u8 = 0xc0; pub const NACK: u8 = 0xa0; -use std::{io::Cursor, ops::Range}; +use std::ops::Range; -use binary_utils::Streamable; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; +use binary_util::BinaryIo; pub(crate) trait Ackable { type NackItem; @@ -24,18 +23,19 @@ pub(crate) trait Ackable { /// An ack record. /// A record holds a single or range of acked packets. /// No real complexity other than that. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] +#[repr(u8)] pub enum Record { - Single(SingleRecord), - Range(RangeRecord), + Single(SingleRecord) = 1, + Range(RangeRecord) = 0, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] pub struct SingleRecord { pub sequence: u32, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] pub struct RangeRecord { pub start: u32, pub end: u32, @@ -119,58 +119,3 @@ impl Ack { return nack; } } - -impl Streamable for Ack { - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut stream: Vec = Vec::new(); - stream.push(self.id); - stream.write_u16::(self.count)?; - - for record in self.records.iter() { - match record { - Record::Single(rec) => { - stream.push(1); - stream.write_u24::(rec.sequence)?; - } - Record::Range(rec) => { - stream.push(0); - stream.write_u24::(rec.start)?; - stream.write_u24::(rec.end)?; - } - } - } - Ok(stream) - } - - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let mut stream = Cursor::new(source); - let id = stream.read_u8().unwrap(); - let count = stream.read_u16::().unwrap(); - let mut records: Vec = Vec::new(); - for _ in 0..count { - if stream.read_u8().unwrap() == 1 { - let record: SingleRecord = SingleRecord { - sequence: stream.read_u24::().unwrap(), - }; - - records.push(Record::Single(record)); - } else { - let mut record: RangeRecord = RangeRecord { - start: stream.read_u24::().unwrap(), - end: stream.read_u24::().unwrap(), - }; - - record.fix(); - - records.push(Record::Range(record)); - } - } - - *position += stream.position() as usize; - - Ok(Self { count, records, id }) - } -} diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 7819f74..03d97cb 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,21 +1,13 @@ use std::io::{Cursor, Write}; -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use binary_util::BinaryIo; /// The information for the given fragment. /// This is used to determine how to reassemble the frame. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] pub struct FragmentMeta { - /// The total number of fragments in this frame. pub(crate) size: u32, - /// The identifier for this fragment. - /// This is used similar to a ordered channel, where the trailing buffer - /// will be stored with this identifier. pub(crate) id: u16, - /// The index of the fragment. - /// This is the arrangement of the fragments in the frame. pub(crate) index: u32, } @@ -23,18 +15,11 @@ use super::reliability::Reliability; /// Frames are a encapsulation of a packet or packets. /// They are used to send packets to the connection in a reliable way. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] pub struct FramePacket { - /// The sequence of this frame. - /// We'll use this to respond with Ack and Nack to. - /// This is sized check to 24 bits. pub sequence: u32, - - /// The frames for this frame packet, not to exceed the mtu size. pub frames: Vec, - - /// This is internal use only. - pub(crate) reliability: Reliability, + pub reliability: Reliability, } impl FramePacket { diff --git a/src/protocol/magic.rs b/src/protocol/magic.rs index 85d1499..4aedeae 100644 --- a/src/protocol/magic.rs +++ b/src/protocol/magic.rs @@ -1,4 +1,6 @@ -use binary_utils::{error::BinaryError, Streamable}; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; +use binary_util::{error::BinaryError, BinaryIo}; /// A unique identifier recoginzing the client as offline. pub(crate) const MAGIC: [u8; 16] = [ @@ -6,31 +8,33 @@ pub(crate) const MAGIC: [u8; 16] = [ ]; #[derive(Debug, Clone)] -pub struct Magic(pub Vec); +pub struct Magic; impl Magic { pub fn new() -> Self { - Self(MAGIC.to_vec()) + Self {} } } -impl Streamable for Magic { - fn parse(&self) -> Result, BinaryError> { - Ok(MAGIC.to_vec()) - } - - fn compose(source: &[u8], position: &mut usize) -> Result { - // magic is 16 bytes - let pos = *position + (16 as usize); - let magic = &source[*position..pos]; - *position += 16; +impl Reader for Magic { + fn read(buf: &mut ByteReader) -> Result { + let mut magic = [0u8; 16]; + buf.read(&mut magic)?; - if magic.to_vec() != MAGIC.to_vec() { - Err(BinaryError::RecoverableKnown( - "Could not construct magic from malformed bytes.".to_string(), - )) - } else { - Ok(Self(magic.to_vec())) + if magic != MAGIC { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid magic", + )); } + + Ok(Magic) + } +} + +impl Writer for Magic { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + buf.write(&MAGIC)?; + Ok(()) } } diff --git a/src/protocol/mcpe/mod.rs b/src/protocol/mcpe/mod.rs index 8881e83..d473c3b 100644 --- a/src/protocol/mcpe/mod.rs +++ b/src/protocol/mcpe/mod.rs @@ -3,19 +3,16 @@ /// display information about the server. pub mod motd; -use crate::packet_id; - -use binary_utils::*; +use binary_util::BinaryIo; use self::motd::Motd; -use super::{packet::PacketId, Magic}; +use super::Magic; -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPong { pub timestamp: u64, pub server_id: u64, pub magic: Magic, pub motd: Motd, } -packet_id!(UnconnectedPong, 0x1c); diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index fac88f9..3870590 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -1,4 +1,5 @@ -use binary_utils::Streamable; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -95,110 +96,112 @@ impl Motd { } } -impl Streamable for Motd { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let motd = String::compose(source, position)?; +impl Reader for Motd { + fn read(buf: &mut ByteReader) -> Result { + let str_len = buf.read_u16()?; + let mut str_buf = vec![0; str_len as usize]; + + buf.read(&mut str_buf)?; + + let motd = String::from_utf8(str_buf).unwrap(); + let parts = motd .split(";") .map(|c| c.to_string()) .collect::>(); + let name = parts .get(1) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd name".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd name", + ))? + .clone(); + let protocol = parts .get(2) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd protocol".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd protocol", + ))? + .clone(); + let version = parts .get(3) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd version".into(), - ))?; - let player_count = - parts - .get(4) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player count".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd version", + ))? + .clone(); + + let player_count = parts + .get(4) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd player count", + ))? + .clone(); + let player_max = parts .get(5) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player maxmium".into(), - ))?; - let server_guid = - parts - .get(6) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd server guid".into(), - ))?; - let _ = parts - .get(7) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd software name".into(), - ))?; - let _ = parts - .get(8) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode string".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd player max", + ))? + .clone(); + + let server_guid = parts + .get(6) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd server guid", + ))? + .clone(); + let gamemode = parts - .get(9) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode".into(), - ))?; + .get(8) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd gamemode", + ))? + .clone(); + let port = parts .get(10) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd port", + ))? + .clone(); + let ipv6_port = parts .get(11) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd ipv6 port", + ))? + .clone(); Ok(Motd { - name: name.clone(), - protocol: protocol - .as_str() - .parse::() - .expect("Invalid motd protocol"), - version: version.clone(), - player_count: player_count - .as_str() - .parse::() - .expect("Player count is not a number"), - player_max: player_max - .as_str() - .parse::() - .expect("Player Maximum is not a number"), - server_guid: server_guid - .as_str() - .parse::() - .expect("Server GUID is not a number"), - port: port.clone(), - ipv6_port: ipv6_port.clone(), - gamemode: match gamemode - .as_str() - .parse::() - .expect("Gamemode is not a byte") - { - 0 => Gamemode::Survival, - 1 => Gamemode::Creative, - 2 => Gamemode::Adventure, - 3 => Gamemode::Spectator, - _ => Gamemode::Survival, - }, + name, + protocol: protocol.parse().unwrap(), + version, + player_count: player_count.parse().unwrap(), + player_max: player_max.parse().unwrap(), + server_guid: server_guid.parse().unwrap(), + gamemode: gamemode.parse().unwrap(), + port, + ipv6_port, }) } +} - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - self.write().parse() +impl Writer for Motd { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + let motd = self.write(); + let motd_len = motd.len() as u16; + buf.write_u16(motd_len)?; + buf.write(motd.as_bytes())?; + Ok(()) } } diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs index d30a7b1..bffd5b7 100644 --- a/src/protocol/packet/mod.rs +++ b/src/protocol/packet/mod.rs @@ -10,343 +10,61 @@ pub mod online; /// This module is used for Pong and connection requests. pub mod offline; -use std::io::Write; - -use binary_utils::Streamable; -use byteorder::WriteBytesExt; - -use self::offline::{ - IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, - SessionInfoRequest, UnconnectedPing, UnconnectedPong, -}; -use self::online::{ - ConnectedPing, ConnectedPong, ConnectionAccept, ConnectionRequest, Disconnect, LostConnection, - NewConnection, -}; +use binary_util::interfaces::{Reader, Writer}; use self::offline::OfflinePacket; use self::online::OnlinePacket; -/// A helper trait to identify packets. -pub trait PacketId { - fn id() -> u8; - - fn get_id(&self) -> u8 { - Self::id() - } -} - -/// A Generic Packet. -/// This is the base for all packets. -#[derive(Clone, Debug)] -pub struct Packet { - /// The packet id. - pub id: u8, - /// The packet data. (this is the payload) - pub payload: Payload, +#[derive(Debug, Clone)] +pub enum RakPacket { + Offline(OfflinePacket), + Online(OnlinePacket), } -impl Packet { +impl RakPacket { pub fn is_online(&self) -> bool { - match self.payload { - Payload::Online(_) => true, + match self { + RakPacket::Online(_) => true, _ => false, } } - pub fn is_offline(&self) -> bool { - return !self.is_online(); - } - - pub fn get_online(&self) -> OnlinePacket { - self.clone().into() - } - - pub fn get_offline(&self) -> OfflinePacket { - self.clone().into() - } -} - -impl Streamable for Packet { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let payload = Payload::compose(source, position)?; - let id = source[0]; - Ok(Packet { id, payload }) - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - buffer.write_u8(self.id)?; - buffer.write_all(&self.payload.parse()?)?; - Ok(buffer) - } -} - -#[derive(Clone, Debug)] -pub enum Payload { - Online(OnlinePacket), - Offline(OfflinePacket), -} - -impl Streamable for Payload { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - // we need the id! - let id = u8::compose(source, position)?; - - match id { - x if x == UnconnectedPing::id() => { - let packet = - OfflinePacket::UnconnectedPing(UnconnectedPing::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == UnconnectedPong::id() => { - let packet = - OfflinePacket::UnconnectedPong(UnconnectedPong::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectRequest::id() => { - let packet = OfflinePacket::OpenConnectRequest(OpenConnectRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectReply::id() => { - let packet = - OfflinePacket::OpenConnectReply(OpenConnectReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoRequest::id() => { - let packet = OfflinePacket::SessionInfoRequest(SessionInfoRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoReply::id() => { - let packet = - OfflinePacket::SessionInfoReply(SessionInfoReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == IncompatibleProtocolVersion::id() => { - let packet = OfflinePacket::IncompatibleProtocolVersion( - IncompatibleProtocolVersion::compose(source, position)?, - ); - Ok(Payload::Offline(packet)) - } - x if x == ConnectedPing::id() => { - let packet = OnlinePacket::ConnectedPing(ConnectedPing::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectedPong::id() => { - let packet = OnlinePacket::ConnectedPong(ConnectedPong::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == LostConnection::id() => { - let packet = - OnlinePacket::LostConnection(LostConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionRequest::id() => { - let packet = - OnlinePacket::ConnectionRequest(ConnectionRequest::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionAccept::id() => { - let packet = - OnlinePacket::ConnectionAccept(ConnectionAccept::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == NewConnection::id() => { - let packet = OnlinePacket::NewConnection(NewConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == Disconnect::id() => { - let packet = OnlinePacket::Disconnect(Disconnect::compose(source, position)?); - Ok(Payload::Online(packet)) - } - _ => Err(binary_utils::error::BinaryError::RecoverableKnown(format!( - "Id is not a valid raknet packet: {}", - id - ))), + pub fn get_offline(&self) -> Option<&OfflinePacket> { + match self { + RakPacket::Offline(packet) => Some(packet), + _ => None, } } - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - // we're not concerned about the id here so we're going to only write the payload! - let payload = match self { - Payload::Online(packet) => match packet { - OnlinePacket::ConnectedPing(pk) => pk.parse()?, - OnlinePacket::ConnectedPong(pk) => pk.parse()?, - OnlinePacket::LostConnection(pk) => pk.parse()?, - OnlinePacket::ConnectionRequest(pk) => pk.parse()?, - OnlinePacket::ConnectionAccept(pk) => pk.parse()?, - OnlinePacket::NewConnection(pk) => pk.parse()?, - OnlinePacket::Disconnect(pk) => pk.parse()?, - }, - Payload::Offline(packet) => match packet { - OfflinePacket::UnconnectedPing(pk) => pk.parse()?, - OfflinePacket::UnconnectedPong(pk) => pk.parse()?, - OfflinePacket::OpenConnectRequest(pk) => pk.parse()?, - OfflinePacket::OpenConnectReply(pk) => pk.parse()?, - OfflinePacket::SessionInfoRequest(pk) => pk.parse()?, - OfflinePacket::SessionInfoReply(pk) => pk.parse()?, - OfflinePacket::IncompatibleProtocolVersion(pk) => pk.parse()?, - }, - }; - if let Err(_) = buffer.write_all(&payload) { - Err(binary_utils::error::BinaryError::RecoverableKnown( - "Failed to write payload to buffer".to_string(), - )) - } else { - Ok(buffer) + pub fn get_online(&self) -> Option<&OnlinePacket> { + match self { + RakPacket::Online(packet) => Some(packet), + _ => None, } } } -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let online: OnlinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OnlinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Online(x) => x, - _ => panic!("This is not an online packet!"), +impl Writer for RakPacket { + fn write(&self, buf: &mut binary_util::io::ByteWriter) -> Result<(), std::io::Error> { + match self { + RakPacket::Offline(packet) => packet.write(buf), + RakPacket::Online(packet) => packet.write(buf), } } } -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let offline: OfflinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OfflinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Offline(x) => x, - _ => panic!("This is not an online packet!"), +impl Reader for RakPacket { + fn read(buf: &mut binary_util::ByteReader) -> Result { + if let Ok(packet) = OfflinePacket::read(buf) { + return Ok(RakPacket::Offline(packet)); } - } -} - -/// This implementation allows for conversion of a OnlinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let payload: Payload = OnlinePacket(_).into(); -/// ``` -impl From for Payload { - fn from(packet: OnlinePacket) -> Self { - Payload::Online(packet) - } -} - -/// This implementation allows for conversion of a OfflinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let payload: Payload = OfflinePacket(_).into(); -/// ``` -impl From for Payload { - fn from(packet: OfflinePacket) -> Self { - Payload::Offline(packet) - } -} - -/// A utility macro to add the `PacketId` trait to a packet. -/// This allows easier decoding of that packet. -#[macro_export] -macro_rules! packet_id { - ($name: ident, $id: literal) => { - impl PacketId for $name { - fn id() -> u8 { - $id - } + if let Ok(packet) = OnlinePacket::read(buf) { + return Ok(RakPacket::Online(packet)); } - }; -} -/// A utility macro that adds the implementation for any `OnlinePacket(Pk)` where -/// `Pk` can be converted to `Packet`, `Payload`, `OnlinePacket` or `OfflinePacket` -/// and vice versa. -/// -/// For example, we want unconnected pong to be unwrapped, we can do this without -/// a match statement like this: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// use raknet::packet::online::UnconnectedPing; -/// let some_packet = Packet::compose(&source, &mut position)?; -/// let connected_ping: UnconnectedPing = some_packet.into(); -/// ``` -/// -/// This macro also allows for converting any `OnlinePacket(Pk)` to a `Packet`, where `Pk` can -/// be directly converted into a packet. For example: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// use raknet::packet::online::UnconnectedPong; -/// -/// let packet: Packet = UnconnectedPong { -/// magic: Magic::new(), -/// timestamp: SystemTime::now(), -/// client_id: -129 -/// }.into(); -/// ``` -#[macro_export] -macro_rules! register_packets { - ($name: ident is $kind: ident, $($packet: ident),*) => { - $( - impl From<$packet> for $kind { - fn from(packet: $packet) -> Self { - $kind::$packet(packet) - } - } - - impl From<$kind> for $packet { - fn from(packet: $kind) -> Self { - match packet { - $kind::$packet(packet) => packet, - _ => panic!("Invalid packet type") - } - } - } - - impl From<$packet> for Packet { - fn from(payload: $packet) -> Self { - Self { - id: payload.get_id(), - payload: Payload::$name(payload.into()), - } - } - } - - impl From<$packet> for Payload { - fn from(payload: $packet) -> Self { - Self::$name(payload.into()) - } - } - - impl From for $packet { - fn from(payload: Payload) -> Self { - match payload { - Payload::$name(v) => v.into(), - _ => panic!("Invalid payload type"), - } - } - } - )* - }; + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid packet", + )) + } } diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index fbc694c..1d3bebd 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -1,123 +1,97 @@ -use std::io::Write; use std::net::SocketAddr; -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::WriteBytesExt; - #[cfg(feature = "mcpe")] pub use crate::protocol::mcpe::UnconnectedPong; -use crate::protocol::RAKNET_HEADER_OVERHEAD; - -use super::Packet; -use super::PacketId; -use super::Payload; use crate::protocol::Magic; -use crate::{packet_id, register_packets}; +use crate::protocol::RAKNET_HEADER_OVERHEAD; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; +use binary_util::BinaryIo; /// A enum that represents all offline packets. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, BinaryIo)] +#[repr(u8)] pub enum OfflinePacket { - UnconnectedPing(UnconnectedPing), - OpenConnectRequest(OpenConnectRequest), - OpenConnectReply(OpenConnectReply), - SessionInfoRequest(SessionInfoRequest), - SessionInfoReply(SessionInfoReply), - #[cfg(feature = "mcpe")] - UnconnectedPong(UnconnectedPong), - #[cfg(not(feature = "mcpe"))] - UnconnectedPong(UnconnectedPong), - IncompatibleProtocolVersion(IncompatibleProtocolVersion), + UnconnectedPing(UnconnectedPing) = 0x01, + OpenConnectRequest(OpenConnectRequest) = 0x05, + OpenConnectReply(OpenConnectReply) = 0x06, + SessionInfoRequest(SessionInfoRequest) = 0x07, + SessionInfoReply(SessionInfoReply) = 0x08, + UnconnectedPong(UnconnectedPong) = 0x1c, + IncompatibleProtocolVersion(IncompatibleProtocolVersion) = 0x19, } -register_packets![ - Offline is OfflinePacket, - UnconnectedPing, - UnconnectedPong, - OpenConnectRequest, - OpenConnectReply, - SessionInfoRequest, - SessionInfoReply, - IncompatibleProtocolVersion -]; - /// Unconnected Ping -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPing { pub timestamp: u64, pub magic: Magic, pub client_id: i64, } -packet_id!(UnconnectedPing, 0x01); /// Unconnected Pong #[cfg(not(feature = "mcpe"))] -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPong { pub timestamp: u64, pub server_id: u64, pub magic: Magic, } -#[cfg(not(feature = "mcpe"))] -packet_id!(UnconnectedPong, 0x1c); /// This packet is the equivelant of the `OpenConnectRequest` packet in RakNet. #[derive(Debug, Clone)] pub struct OpenConnectRequest { - pub magic: Magic, pub protocol: u8, // 9 pub mtu_size: u16, // 500 } -impl Streamable for OpenConnectRequest { - fn compose(source: &[u8], position: &mut usize) -> Result { - Ok(Self { - magic: Magic::compose(source, position)?, - protocol: u8::compose(source, position)?, - mtu_size: (source.len() + 1 + 28) as u16, + +impl Reader for OpenConnectRequest { + fn read(buf: &mut ByteReader) -> Result { + let len = buf.as_slice().len(); + buf.read_struct::()?; + Ok(OpenConnectRequest { + protocol: buf.read_u8()?, + mtu_size: (len + 1 + 28) as u16, }) } +} - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::::new(); - stream - .write(&self.magic.parse()?[..]) - .expect("Failed to parse open connect request"); - stream.write_u8(self.protocol)?; +impl Writer for OpenConnectRequest { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + buf.write_struct::()?; + buf.write_u8(self.protocol)?; // padding // remove 28 bytes from the mtu size - let mtu_size = self.mtu_size - stream.len() as u16 - RAKNET_HEADER_OVERHEAD as u16; + let mtu_size = self.mtu_size - buf.as_slice().len() as u16 - RAKNET_HEADER_OVERHEAD as u16; for _ in 0..mtu_size { - stream.write_u8(0)?; + buf.write_u8(0)?; } - Ok(stream) + Ok(()) } } -packet_id!(OpenConnectRequest, 0x05); // Open Connection Reply /// Sent to the client when the server accepts a client. /// This packet is the equivalent of the `Open Connect Reply 1` packet. -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct OpenConnectReply { pub magic: Magic, pub server_id: u64, pub security: bool, pub mtu_size: u16, } -packet_id!(OpenConnectReply, 0x06); /// Session info, also known as Open Connect Request 2 -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoRequest { pub magic: Magic, pub address: SocketAddr, pub mtu_size: u16, pub client_id: i64, } -packet_id!(SessionInfoRequest, 0x07); /// Session Info Reply, also known as Open Connect Reply 2 -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoReply { pub magic: Magic, pub server_id: u64, @@ -125,12 +99,10 @@ pub struct SessionInfoReply { pub mtu_size: u16, pub security: bool, } -packet_id!(SessionInfoReply, 0x08); -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct IncompatibleProtocolVersion { pub protocol: u8, pub magic: Magic, pub server_id: u64, } -packet_id!(IncompatibleProtocolVersion, 0x19); diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index 5eb73c1..0755ac5 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -1,69 +1,45 @@ -use std::io::Cursor; -use std::io::Write; -use std::net::IpAddr; -use std::net::Ipv4Addr; use std::net::SocketAddr; -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::BigEndian; -use byteorder::ReadBytesExt; -use byteorder::WriteBytesExt; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; +use binary_util::BinaryIo; -use super::Packet; -use super::PacketId; -use super::Payload; -use crate::{packet_id, register_packets}; - -/// A enum that represents all online packets. -#[derive(Clone, Debug)] +#[derive(BinaryIo, Clone, Debug)] +#[repr(u8)] pub enum OnlinePacket { - ConnectedPing(ConnectedPing), - ConnectedPong(ConnectedPong), - LostConnection(LostConnection), - ConnectionRequest(ConnectionRequest), - ConnectionAccept(ConnectionAccept), - NewConnection(NewConnection), - Disconnect(Disconnect), + ConnectedPing(ConnectedPing) = 0x00, + ConnectedPong(ConnectedPong) = 0x03, + LostConnection(LostConnection) = 0x04, + ConnectionRequest(ConnectionRequest) = 0x09, + ConnectionAccept(ConnectionAccept) = 0x10, + NewConnection(NewConnection) = 0x13, + Disconnect(Disconnect) = 0x15, } -register_packets![ - Online is OnlinePacket, - ConnectedPing, - ConnectedPong, - ConnectionRequest, - ConnectionAccept, - NewConnection, - Disconnect -]; - /// Connected Ping Packet /// This packet is sent by the client to the server. /// The server should respond with a `ConnectedPong` packet. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPing { pub time: i64, } -packet_id!(ConnectedPing, 0x00); /// Connected Pong Packet /// This packet is sent by the server to the client in response to a `ConnectedPing` packet. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPong { pub ping_time: i64, pub pong_time: i64, } -packet_id!(ConnectedPong, 0x03); /// A connection Request Request, this contains information about the client. Like it's /// current time and the client id. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectionRequest { pub client_id: i64, pub time: i64, pub security: bool, } -packet_id!(ConnectionRequest, 0x09); /// A connection Accept packet, this is sent by the server to the client. /// This is sent by the server and contains information about the server. @@ -71,44 +47,69 @@ packet_id!(ConnectionRequest, 0x09); pub struct ConnectionAccept { /// The address of the client connecting (locally?). pub client_address: SocketAddr, - /// The system index is the index of the system that the client is connected to. - /// This is the index of the server on the client. - /// (Not sure why this is useful) + /// The system index of the server. pub system_index: i16, /// The internal id's of the server or alternative IP's of the server. /// These are addresses the client will use if it can't connect to the server. /// (Not sure why this is useful) - pub internal_id: SocketAddr, + pub internal_ids: Vec, /// The time of the timestamp the client sent with `ConnectionRequest`. pub request_time: i64, /// The time on the server. pub timestamp: i64, } -impl Streamable for ConnectionAccept { - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::new(); - stream.write_all(&self.client_address.parse()?[..])?; - stream.write_i16::(self.system_index)?; - for _ in 0..10 { - stream.write_all(&self.internal_id.parse()?[..])?; +impl Reader for ConnectionAccept { + fn read(buf: &mut ByteReader) -> std::io::Result { + let client_address = buf.read_struct::()?; + + // read the system index, this is + let system_index = buf.read_i16()?; + let internal_ids = Vec::new::(); + + for _ in 0..20 { + // we only have the request time and timestamp left... + if buf.len() < 16 { + break; + } + internal_ids.push(buf.read_struct::()?); } - stream.write_i64::(self.request_time)?; - stream.write_i64::(self.timestamp)?; - Ok(stream) - } - fn compose(_source: &[u8], _position: &mut usize) -> Result { + let request_time = buf.read_i64()?; + let timestamp = buf.read_i64()?; + Ok(Self { - client_address: SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120), - system_index: 0, - internal_id: SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), 1920), - request_time: 0, - timestamp: 0, + client_address, + system_index, + internal_ids, + request_time, + timestamp, }) } } -packet_id!(ConnectionAccept, 0x10); + +impl Writer for ConnectionAccept { + fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { + buf.write_struct(&self.client_address)?; + buf.write_i16(self.system_index)?; + + if self.internal_ids.len() > 20 { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Too many internal id's", + )); + } + + for internal_id in &self.internal_ids { + buf.write_struct(internal_id)?; + } + + buf.write_i64(self.request_time)?; + buf.write_i64(self.timestamp)?; + + Ok(()) + } +} /// Going to be completely Honest here, I have no idea what this is used for right now, /// even after reading the source code. @@ -123,29 +124,24 @@ pub struct NewConnection { /// The time on the server. pub timestamp: i64, } -packet_id!(NewConnection, 0x13); - -impl Streamable for NewConnection { - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::new(); - stream.write_all(&self.server_address.parse()?[..])?; - for address in &self.system_address { - stream.write_all(&address.parse()?[..])?; - } - stream.write_i64::(self.request_time)?; - stream.write_i64::(self.timestamp)?; - Ok(stream) - } - fn compose(source: &[u8], position: &mut usize) -> Result { - let server_address = SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120); - let mut stream = Cursor::new(source); - let mut system_address = Vec::new(); - for _ in 0..10 { - system_address.push(SocketAddr::compose(source, position)?); +impl Reader for NewConnection { + fn read(buf: &mut ByteReader) -> std::io::Result { + let server_address = buf.read_struct::()?; + + let system_address = Vec::new::(); + + for _ in 0..20 { + // we only have the request time and timestamp left... + if buf.len() < 16 { + break; + } + system_address.push(buf.read_struct::()?); } - let request_time = stream.read_i64::()?; - let timestamp = stream.read_i64::()?; + + let request_time = buf.read_i64()?; + let timestamp = buf.read_i64()?; + Ok(Self { server_address, system_address, @@ -155,13 +151,33 @@ impl Streamable for NewConnection { } } +impl Writer for NewConnection { + fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { + buf.write_struct(&self.server_address)?; + + if self.system_address.len() > 20 { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Too many internal id's", + )); + } + + for system_address in &self.system_address { + buf.write_struct(system_address)?; + } + + buf.write_i64(self.request_time)?; + buf.write_i64(self.timestamp)?; + + Ok(()) + } +} + /// A disconnect notification. Tells the client to disconnect. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct Disconnect {} -packet_id!(Disconnect, 0x15); /// A connection lost notification. /// This is sent by the client when it loses connection to the server. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct LostConnection {} -packet_id!(LostConnection, 0x04); diff --git a/src/server/mod.rs b/src/server/mod.rs index 4b853c5..7ad0e33 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -14,8 +14,10 @@ use async_std::{ sync::Mutex, task::{self}, }; -use binary_utils::Streamable; +use binary_util::ByteReader; +use binary_util::ByteWriter; +use binary_util::interfaces::Writer; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -31,7 +33,7 @@ use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, }; -use crate::protocol::packet::{Packet, Payload}; +use crate::protocol::packet::RakPacket; use crate::protocol::Magic; use crate::rakrs_debug; use crate::util::to_address_token; @@ -234,181 +236,174 @@ impl Listener { } // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally - if let Ok(packet) = Packet::compose(&mut buf, &mut 0) { - // if this is an offline packet, we can retrieve the buffer we should send. - match packet.payload { - Payload::Offline(pk) => { - // Offline packets are not buffered to the user. - // The reason for this is because we don't wish for the user to be able to disrupt - // raknet protocol, and handshaking. - match pk { - OfflinePacket::UnconnectedPing(_) => { - // let (resp_tx, resp_rx) = - // oneshot::channel::(); - #[cfg(feature = "mcpe")] - let motd: Motd = motd_default.clone(); - - // if let Err(e) = send_evt.try_send(( - // ServerEvent::RefreshMotdRequest(origin, motd.clone()), - // // resp_tx, - // )) - // { - // match e { - // TrySendError::Full(_) => { - // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); - // } - // TrySendError::Closed(_) => { - // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); - // } - // } - // } - - // if let Ok(res) = resp_rx.await { - // // get the motd from the server event otherwise use defaults. - // // if let Ok(res) = res { - // match res { - // ServerEventResponse::RefreshMotd(m) => { - // motd = m; - // } - // _ => { - // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); - // } - // } - // // }; - // } - - // unconnected pong signature is different if MCPE is specified. - let resp = UnconnectedPong { - timestamp: current_epoch(), - server_id, - magic: Magic::new(), - #[cfg(feature = "mcpe")] - motd, - }; - - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; - } - OfflinePacket::OpenConnectRequest(mut pk) => { - // TODO make a constant for this - if !versions.contains(&pk.protocol) { - let resp = IncompatibleProtocolVersion { - protocol: pk.protocol, - magic: Magic::new(), - server_id, - }; - - rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); - - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; - } - - rakrs_debug!( - true, - "[{}] Client requested Mtu Size: {}", - to_address_token(*&origin), - pk.mtu_size - ); - - if pk.mtu_size > 2048 { - rakrs_debug!( - true, - "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", - to_address_token(*&origin), - pk.mtu_size - ); - pk.mtu_size = 2048; - } - - let resp = OpenConnectReply { - server_id, - // TODO allow encryption - security: false, - magic: Magic::new(), - // TODO make this configurable, this is sent to the client to change - // it's mtu size, right now we're using what the client prefers. - // however in some cases this may not be the preferred use case, for instance - // on servers with larger worlds, you may want a larger mtu size, or if - // your limited on network bandwith - mtu_size: pk.mtu_size, - }; - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; - } - OfflinePacket::SessionInfoRequest(pk) => { - let resp = SessionInfoReply { - server_id, - client_address: origin, - magic: Magic::new(), - mtu_size: pk.mtu_size, - security: false, - }; - - // This is a valid packet, let's check if a session exists, if not, we should create it. - // Event if the connection is only in offline mode. - let mut sessions = connections.lock().await; - - if !sessions.contains_key(&origin) { - rakrs_debug!(true, "Creating new session for {}", origin); - let meta = ConnMeta::new(0); - let (net_send, net_recv) = bounded::>(10); - let connection = - Connection::new(origin, &socket, net_recv, pk.mtu_size) - .await; - rakrs_debug!(true, "Created Session for {}", origin); - - // Add the connection to the available connections list. - // we're using the name "sessions" here to differeniate - // for some reason the reciever likes to be dropped, so we're saving it here. - sessions.insert(origin, (meta, net_send)); - - // notify the connection communicator - if let Err(err) = send_comm.send(connection).await { - let connection = err.0; - // there was an error, and we should terminate this connection immediately. - rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); - sessions.remove(&origin); - continue; - } - } - - // update the sessions mtuSize, this is referred to internally, we also will send this event to the client - // event channel. However we are not expecting a response. - - sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; - - // let (resp_tx, resp_rx) = oneshot::channel::(); - - // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { - // rakrs_debug!( - // "[{}] Failed to update mtu size with the client!", - // to_address_token(origin) - // ); - // } - - // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) - // .await - // { - // rakrs_debug!( - // "[{}] Failed to update mtu size with the client!", - // to_address_token(origin) - // ); - // } - - send_packet_to_socket(&socket, resp.into(), origin).await; + if let Ok(pk) = OfflinePacket::read(ByteReader::new(&buf[..length])) { + // Offline packets are not buffered to the user. + // The reason for this is because we don't wish for the user to be able to disrupt + // raknet protocol, and handshaking. + match pk { + OfflinePacket::UnconnectedPing(_) => { + // let (resp_tx, resp_rx) = + // oneshot::channel::(); + #[cfg(feature = "mcpe")] + let motd: Motd = motd_default.clone(); + + // if let Err(e) = send_evt.try_send(( + // ServerEvent::RefreshMotdRequest(origin, motd.clone()), + // // resp_tx, + // )) + // { + // match e { + // TrySendError::Full(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); + // } + // TrySendError::Closed(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); + // } + // } + // } + + // if let Ok(res) = resp_rx.await { + // // get the motd from the server event otherwise use defaults. + // // if let Ok(res) = res { + // match res { + // ServerEventResponse::RefreshMotd(m) => { + // motd = m; + // } + // _ => { + // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); + // } + // } + // // }; + // } + + // unconnected pong signature is different if MCPE is specified. + let resp = UnconnectedPong { + timestamp: current_epoch(), + server_id, + magic: Magic::new(), + #[cfg(feature = "mcpe")] + motd, + }; + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + OfflinePacket::OpenConnectRequest(mut pk) => { + // TODO make a constant for this + if !versions.contains(&pk.protocol) { + let resp = IncompatibleProtocolVersion { + protocol: pk.protocol, + magic: Magic::new(), + server_id, + }; + + rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {}", + to_address_token(*&origin), + pk.mtu_size + ); + + if pk.mtu_size > 2048 { + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", + to_address_token(*&origin), + pk.mtu_size + ); + pk.mtu_size = 2048; + } + + let resp = OpenConnectReply { + server_id, + // TODO allow encryption + security: false, + magic: Magic::new(), + // TODO make this configurable, this is sent to the client to change + // it's mtu size, right now we're using what the client prefers. + // however in some cases this may not be the preferred use case, for instance + // on servers with larger worlds, you may want a larger mtu size, or if + // your limited on network bandwith + mtu_size: pk.mtu_size, + }; + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + OfflinePacket::SessionInfoRequest(pk) => { + let resp = SessionInfoReply { + server_id, + client_address: origin, + magic: Magic::new(), + mtu_size: pk.mtu_size, + security: false, + }; + + // This is a valid packet, let's check if a session exists, if not, we should create it. + // Event if the connection is only in offline mode. + let mut sessions = connections.lock().await; + + if !sessions.contains_key(&origin) { + rakrs_debug!(true, "Creating new session for {}", origin); + let meta = ConnMeta::new(0); + let (net_send, net_recv) = bounded::>(10); + let connection = + Connection::new(origin, &socket, net_recv, pk.mtu_size).await; + rakrs_debug!(true, "Created Session for {}", origin); + + // Add the connection to the available connections list. + // we're using the name "sessions" here to differeniate + // for some reason the reciever likes to be dropped, so we're saving it here. + sessions.insert(origin, (meta, net_send)); + + // notify the connection communicator + if let Err(err) = send_comm.send(connection).await { + let connection = err.0; + // there was an error, and we should terminate this connection immediately. + rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); + sessions.remove(&origin); continue; } - _ => { - rakrs_debug!( - "[{}] Received invalid packet!", - to_address_token(*&origin) - ); - } } + + // update the sessions mtuSize, this is referred to internally, we also will send this event to the client + // event channel. However we are not expecting a response. + + sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; + + // let (resp_tx, resp_rx) = oneshot::channel::(); + + // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) + // .await + // { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; } - _ => {} - }; + _ => { + rakrs_debug!( + "[{}] Received invalid packet!", + to_address_token(*&origin) + ); + } + } } // Packet may be valid, but we'll let the connection decide this @@ -487,9 +482,9 @@ impl Listener { } } -async fn send_packet_to_socket(socket: &Arc, packet: Packet, origin: SocketAddr) { +async fn send_packet_to_socket(socket: &Arc, packet: RakPacket, origin: SocketAddr) { if let Err(e) = socket - .send_to(&mut packet.parse().unwrap()[..], origin) + .send_to(&mut packet.write_to_bytes().unwrap().as_slice(), origin) .await { rakrs_debug!( From 76645ba4b3d20217080c55889549afc51c85e739 Mon Sep 17 00:00:00 2001 From: john-bv Date: Wed, 9 Aug 2023 22:37:29 -0500 Subject: [PATCH 67/75] chore: more updates [ci skip] --- src/client/handshake.rs | 141 ++++++++++++++++---------------- src/client/mod.rs | 10 +-- src/protocol/frame.rs | 164 ++++++++++++-------------------------- src/protocol/magic.rs | 1 - src/protocol/mcpe/motd.rs | 14 +++- src/server/mod.rs | 5 +- 6 files changed, 138 insertions(+), 197 deletions(-) diff --git a/src/client/handshake.rs b/src/client/handshake.rs index 72832dc..d4b5269 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -10,6 +10,7 @@ use async_std::{ task::{self, Context, Poll, Waker}, }; +use binary_util::ByteReader; use binary_util::Streamable; #[cfg(feature = "async_tokio")] use std::future::Future; @@ -25,14 +26,13 @@ use tokio::{ use crate::connection::queue::send::SendQueue; use crate::connection::queue::RecvQueue; use crate::protocol::frame::FramePacket; +use crate::protocol::packet::RakPacket; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, SessionInfoRequest, }; use crate::protocol::packet::online::ConnectedPong; use crate::protocol::packet::online::{ConnectionRequest, NewConnection, OnlinePacket}; -use crate::protocol::packet::Packet; -use crate::protocol::packet::PacketId; use crate::protocol::reliability::Reliability; use crate::protocol::Magic; use crate::rakrs_debug; @@ -178,7 +178,6 @@ impl ClientHandshake { // todo: continously send untill we get a reply // todo: we also need to decrease the MTU until we get a reply let connect_request = OpenConnectRequest { - magic: Magic::new(), protocol: version, mtu_size: mtu, }; @@ -295,85 +294,83 @@ impl ClientHandshake { // proccess frame packet match buf[0] { 0x80..=0x8d => { - if let Ok(pk) = FramePacket::compose(&mut buf[..len], &mut 0) { + if let Ok(pk) = FramePacket::read(&mut buf[..len]) { recv_q.insert(pk).unwrap(); let raw_packets = recv_q.flush(); for mut raw_pk in raw_packets { - let pk = Packet::compose(&mut raw_pk[..], &mut 0); - - if let Ok(pk) = pk { - if pk.is_online() { - match pk.get_online() { - OnlinePacket::ConnectedPing(pk) => { + let pk = ByteReader::from(&mut raw_pk[..]); + + if let Ok(pk) = OnlinePacket::read(&mut pk) { + match pk { + OnlinePacket::ConnectedPing(pk) => { + rakrs_debug!( + true, + "[CLIENT] Received ConnectedPing from server!" + ); + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + + if let Err(_) = send_q + .send_packet( + response.into(), + Reliability::Reliable, + true, + ) + .await + { rakrs_debug!( true, - "[CLIENT] Received ConnectedPing from server!" + "[CLIENT] Failed to send pong packet!" ); - let response = ConnectedPong { - ping_time: pk.time, - pong_time: current_epoch() as i64, - }; - - if let Err(_) = send_q - .send_packet( - response.into(), - Reliability::Reliable, - true, - ) - .await - { - rakrs_debug!( - true, - "[CLIENT] Failed to send pong packet!" - ); - } - - continue; } - OnlinePacket::ConnectionAccept(pk) => { - // send new incoming connection - let new_incoming = NewConnection { - server_address: socket.peer_addr().unwrap(), - system_address: vec![ - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - socket.peer_addr().unwrap(), - ], - request_time: pk.request_time, - timestamp: pk.timestamp, - }; - if let Err(_) = send_q - .send_packet( - new_incoming.into(), - Reliability::Reliable, - true, - ) - .await - { - update_state!( - true, - shared_state, - HandshakeStatus::Failed - ); - } else { - update_state!( - true, - shared_state, - HandshakeStatus::Completed - ); - } + + continue; + } + OnlinePacket::ConnectionAccept(pk) => { + // send new incoming connection + let new_incoming = NewConnection { + server_address: socket.peer_addr().unwrap(), + system_address: vec![ + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + ], + request_time: pk.request_time, + timestamp: pk.timestamp, + }; + if let Err(_) = send_q + .send_packet( + new_incoming.into(), + Reliability::Reliable, + true, + ) + .await + { + update_state!( + true, + shared_state, + HandshakeStatus::Failed + ); + } else { + update_state!( + true, + shared_state, + HandshakeStatus::Completed + ); } - _ => {} } + _ => {} } } } @@ -404,7 +401,7 @@ impl Future for ClientHandshake { } } -async fn send_packet(socket: &Arc, packet: Packet) -> bool { +async fn send_packet(socket: &Arc, packet: RakPacket) -> bool { if let Err(e) = socket .send_to( &mut packet.parse().unwrap()[..], diff --git a/src/client/mod.rs b/src/client/mod.rs index 35203d3..cf54c03 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -18,7 +18,7 @@ use async_std::{ #[cfg(feature = "async_std")] use futures::{select, FutureExt}; -use binary_util::Streamable; +use binary_util::interfaces::{Reader, Writer}; #[cfg(feature = "async_tokio")] use tokio::{ @@ -48,7 +48,7 @@ use crate::{ packet::{ offline::{OfflinePacket, UnconnectedPing}, online::{ConnectedPing, ConnectedPong, OnlinePacket}, - Packet, + RakPacket, }, reliability::Reliability, Magic, @@ -419,7 +419,7 @@ impl Client { }; if let Err(_) = socket - .send(&Packet::from(unconnected_ping.clone()).parse().unwrap()[..]) + .send(RakPacket::Offline(OfflinePacket::UnconnectedPing(unconnected_ping))) .await { rakrs_debug!(true, "[CLIENT] Failed to send ping packet!"); @@ -431,7 +431,7 @@ impl Client { if let Ok(recvd) = timeout(Duration::from_millis(10000), socket.recv(&mut buf)).await { match recvd { Ok(l) => { - let packet = Packet::compose(&mut buf[..l], &mut 0).unwrap(); + let packet = OfflinePacket::read(&mut buf[..l]).unwrap(); if packet.is_offline() { match packet.get_offline() { OfflinePacket::UnconnectedPong(pk) => { @@ -536,7 +536,7 @@ impl Client { let buffers = recv_q.flush(); 'buf_loop: for mut pk_buf in buffers { - if let Ok(packet) = Packet::compose(&mut pk_buf[..], &mut 0) { + if let Ok(packet) = RakPacket::read(&mut pk_buf[..], &mut 0) { if packet.is_online() { match packet.get_online() { OnlinePacket::ConnectedPing(pk) => { diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index 03d97cb..ca4605c 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,6 +1,6 @@ use std::io::{Cursor, Write}; -use binary_util::BinaryIo; +use binary_util::{BinaryIo, interfaces::{Reader, Writer}}; /// The information for the given fragment. /// This is used to determine how to reassemble the frame. @@ -15,7 +15,7 @@ use super::reliability::Reliability; /// Frames are a encapsulation of a packet or packets. /// They are used to send packets to the connection in a reliable way. -#[derive(Debug, Clone, BinaryIo)] +#[derive(Debug, Clone)] pub struct FramePacket { pub sequence: u32, pub frames: Vec, @@ -33,59 +33,37 @@ impl FramePacket { } } -impl Streamable for FramePacket { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source); - stream.set_position(*position as u64); - stream.read_u8()?; +impl Reader for FramePacket { + fn read(buf: &mut binary_util::ByteReader) -> Result { let mut frames: Vec = Vec::new(); - let sequence = stream.read_u24::()?; - let mut offset: usize = stream.position() as usize; + let sequence = buf.read_u24_le()?; loop { - if stream.position() > source.len() as u64 { - return Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - }); - } - - if stream.position() == source.len() as u64 { - break Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - }); - } - - if let Ok(frame) = Frame::compose(&source, &mut offset) { - stream.set_position(offset as u64); - frames.push(frame.clone()); - - // if frame.parse()?.len() + stream.position() as usize > source.len() { - // return Ok(FramePacket { sequence, frames, byte_length: 0 }); - // } else { - // continue; - // } + if let Ok(frame) = buf.read_struct::() { + frames.push(frame); } else { - return Err(BinaryError::RecoverableKnown( - "Frame composition failed! Failed to read frame.".into(), - )); + break; } } + + Ok(FramePacket { + sequence, + frames, + reliability: Reliability::ReliableOrd, + }) } +} - fn parse(&self) -> Result, BinaryError> { - let mut stream = Cursor::new(Vec::new()); - stream.write_u8(0x80)?; - stream.write_u24::(self.sequence)?; +impl Writer for FramePacket { + fn write(&self, buf: &mut binary_util::ByteWriter) -> Result<(), std::io::Error> { + buf.write_u8(0x80)?; + buf.write_u24_le(self.sequence)?; for frame in &self.frames { - stream.write_all(&frame.parse()?)?; + buf.write(frame.write_to_bytes()?.as_slice())?; } - Ok(stream.into_inner()) + Ok(()) } } @@ -168,105 +146,63 @@ impl Frame { } } -impl Streamable for Frame { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source.to_vec()); +impl Reader for Frame { + fn read(buf: &mut binary_util::ByteReader) -> Result { + let mut frame = Frame::init(); - // create a dummy frame for us to write to. - let mut frame: Frame = Frame::init(); - - // set the position to the current position - stream.set_position(*position as u64); - - // read the flags - frame.flags = stream.read_u8()?; - // set the reliability + frame.flags = buf.read_u8()?; frame.reliability = Reliability::from_flags(frame.flags); + frame.size = buf.read_u16()? / 8; - // read the length of the body in bits - frame.size = stream.read_u16::()? / 8; - - // check whether or not this frame is reliable, if it is, read the reliable index if frame.reliability.is_reliable() { - frame.reliable_index = Some(stream.read_u24::()?); + frame.reliable_index = Some(buf.read_u24_le()?); } - // check whether or not this frame is sequenced, if it is, read the sequenced index if frame.reliability.is_sequenced() { - frame.sequence_index = Some(stream.read_u24::()?); + frame.sequence_index = Some(buf.read_u24_le()?); } - // check whether or not this frame is ordered, if it is, read the order index - // and order channel - if frame.reliability.is_sequenced_or_ordered() { - frame.order_index = Some(stream.read_u24::()?); - frame.order_channel = Some(stream.read_u8()?); + if frame.reliability.is_ordered() { + frame.order_index = Some(buf.read_u24_le()?); + frame.order_channel = Some(buf.read_u8()?); } - // check whether or not this frame is fragmented, if it is, read the fragment meta - if (frame.flags & 0x10) > 0 { - frame.fragment_meta = Some(FragmentMeta { - size: stream.read_u32::()?.try_into().unwrap(), - id: stream.read_u16::()?, - index: stream.read_u32::()?.try_into().unwrap(), - }); + if frame.flags & 0x10 == 0x10 { + frame.fragment_meta = Some(FragmentMeta::read(buf)?); } - // read the body - frame.body = (&source - [stream.position() as usize..stream.position() as usize + frame.size as usize]) - .to_vec(); - // update the position. - *position = stream.position() as usize + frame.size as usize; + let body = [0; frame.size]; + + frame.body = buf.read(&)?; Ok(frame) } +} - fn parse(&self) -> Result, error::BinaryError> { - let mut stream = Cursor::new(Vec::new()); - // generate the flags! - let mut flags = self.reliability.to_flags(); - - // check whether or not this frame is fragmented, if it is, set the fragment flag - if self.fragment_meta.is_some() { - flags |= 0x10; - } - - let size = self.body.len() as u16; - - // write the flags - stream.write_u8(flags)?; - // write the length of the body in bits - stream.write_u16::(size * 8)?; +impl Writer for Frame { + fn write(&self, buf: &mut binary_util::ByteWriter) -> Result<(), std::io::Error> { + buf.write_u8(self.flags)?; + buf.write_u16(self.size * 8)?; - // check whether or not this frame is reliable, if it is, write the reliable index if self.reliability.is_reliable() { - stream.write_u24::(self.reliable_index.unwrap())?; + buf.write_u24_le(self.reliable_index.unwrap_or(0))?; } - // check whether or not this frame is sequenced, if it is, write the sequenced index if self.reliability.is_sequenced() { - stream.write_u24::(self.sequence_index.unwrap())?; + buf.write_u24_le(self.sequence_index.unwrap_or(0))?; } - // check whether or not this frame is ordered, if it is, write the order index - // and order channel - if self.reliability.is_sequenced_or_ordered() { - stream.write_u24::(self.order_index.unwrap())?; - stream.write_u8(self.order_channel.unwrap_or(0))?; + if self.reliability.is_ordered() { + buf.write_u24_le(self.order_index.unwrap_or(0))?; + buf.write_u8(self.order_channel.unwrap_or(0))?; } - // check whether or not this frame is fragmented, if it is, write the fragment meta - if self.fragment_meta.is_some() { - let fragment_meta = self.fragment_meta.as_ref().unwrap(); - stream.write_u32::(fragment_meta.size.try_into().unwrap())?; - stream.write_u16::(fragment_meta.id)?; - stream.write_u32::(fragment_meta.index.try_into().unwrap())?; + if self.flags & 0x10 == 0x10 { + self.fragment_meta.as_ref().unwrap().write(buf)?; } - // write the body - stream.write_all(&self.body)?; + buf.write(&self.body)?; - Ok(stream.get_ref().clone()) + Ok(()) } } diff --git a/src/protocol/magic.rs b/src/protocol/magic.rs index 4aedeae..3942d79 100644 --- a/src/protocol/magic.rs +++ b/src/protocol/magic.rs @@ -1,6 +1,5 @@ use binary_util::interfaces::{Reader, Writer}; use binary_util::io::{ByteReader, ByteWriter}; -use binary_util::{error::BinaryError, BinaryIo}; /// A unique identifier recoginzing the client as offline. pub(crate) const MAGIC: [u8; 16] = [ diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index 3870590..865bb1c 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -184,12 +184,22 @@ impl Reader for Motd { Ok(Motd { name, - protocol: protocol.parse().unwrap(), + protocol: protocol.as_str().parse().unwrap(), version, player_count: player_count.parse().unwrap(), player_max: player_max.parse().unwrap(), server_guid: server_guid.parse().unwrap(), - gamemode: gamemode.parse().unwrap(), + gamemode: match gamemode + .as_str() + .parse::() + .expect("Gamemode is not a byte") + { + 0 => Gamemode::Survival, + 1 => Gamemode::Creative, + 2 => Gamemode::Adventure, + 3 => Gamemode::Spectator, + _ => Gamemode::Survival, + }, port, ipv6_port, }) diff --git a/src/server/mod.rs b/src/server/mod.rs index 7ad0e33..4e0ef7a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -15,9 +15,8 @@ use async_std::{ task::{self}, }; use binary_util::ByteReader; -use binary_util::ByteWriter; -use binary_util::interfaces::Writer; +use binary_util::interfaces::{Reader, Writer}; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -236,7 +235,7 @@ impl Listener { } // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally - if let Ok(pk) = OfflinePacket::read(ByteReader::new(&buf[..length])) { + if let Ok(pk) = OfflinePacket::read(&mut ByteReader::from(&buf[..length])) { // Offline packets are not buffered to the user. // The reason for this is because we don't wish for the user to be able to disrupt // raknet protocol, and handshaking. From 8b5c90e5631aa276d40dafcfd9b42948f147cd25 Mon Sep 17 00:00:00 2001 From: Bavfalcon9 Date: Fri, 11 Aug 2023 06:23:51 -0500 Subject: [PATCH 68/75] fix(notifiers): They now work on both async and tokio --- Cargo.toml | 4 ++-- examples/async-std/server/src/main.rs | 4 +++- src/notify/mod.rs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 83e002c..fb8732a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Bavfalcon9 "] edition = "2021" [features] -# default = [ "async_std" ] -default = ["async_tokio" ] +default = [ "async_std" ] +# default = ["async_tokio" ] mcpe = [] debug = [] debug_all = [] diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs index 34b2d00..013aaf9 100644 --- a/examples/async-std/server/src/main.rs +++ b/examples/async-std/server/src/main.rs @@ -5,9 +5,11 @@ use rak_rs::Listener; #[async_std::main] async fn main() { // console_subscriber::init(); - let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + let mut server = Listener::bind("0.0.0.0:19133").await.unwrap(); server.motd.name = "RakNet Rust (async-std)!".to_string(); server.motd.gamemode = Gamemode::Survival; + server.motd.player_count = 69420; + server.motd.player_max = 69424; server.start().await.unwrap(); diff --git a/src/notify/mod.rs b/src/notify/mod.rs index 5dcf9df..c7fa4cb 100644 --- a/src/notify/mod.rs +++ b/src/notify/mod.rs @@ -5,7 +5,7 @@ mod async_std; mod tokio; #[cfg(feature = "async_std")] -pub use async_std::Notify; +pub use self::async_std::Notify; #[cfg(feature = "tokio")] -pub use tokio::Notify; +pub use self::tokio::Notify; From fb0fcecaae385d5b2b51d8a3f34142a8ccd823ba Mon Sep 17 00:00:00 2001 From: john-bv Date: Sat, 12 Aug 2023 18:22:21 -0500 Subject: [PATCH 69/75] feat: binary_utils v3! --- .../resources}/img/Connection Handshake.png | Bin .../resources}/mddoc/Raknet.md | 0 .../resources}/mddoc/protocol/README.md | 0 .../resources}/mddoc/reliability.md | 0 Cargo.toml | 4 +- src/client/handshake.rs | 60 +++--- src/client/mod.rs | 174 +++++++++--------- src/connection/mod.rs | 70 ++++--- src/connection/queue/mod.rs | 2 +- src/connection/queue/send.rs | 24 ++- src/protocol/ack.rs | 3 +- src/protocol/frame.rs | 53 ++++-- src/protocol/mcpe/motd.rs | 2 +- src/protocol/packet/mod.rs | 103 +++++++++++ src/protocol/packet/offline.rs | 18 +- src/protocol/packet/online.rs | 30 ++- 16 files changed, 371 insertions(+), 172 deletions(-) rename {resources => .github/resources}/img/Connection Handshake.png (100%) rename {resources => .github/resources}/mddoc/Raknet.md (100%) rename {resources => .github/resources}/mddoc/protocol/README.md (100%) rename {resources => .github/resources}/mddoc/reliability.md (100%) diff --git a/resources/img/Connection Handshake.png b/.github/resources/img/Connection Handshake.png similarity index 100% rename from resources/img/Connection Handshake.png rename to .github/resources/img/Connection Handshake.png diff --git a/resources/mddoc/Raknet.md b/.github/resources/mddoc/Raknet.md similarity index 100% rename from resources/mddoc/Raknet.md rename to .github/resources/mddoc/Raknet.md diff --git a/resources/mddoc/protocol/README.md b/.github/resources/mddoc/protocol/README.md similarity index 100% rename from resources/mddoc/protocol/README.md rename to .github/resources/mddoc/protocol/README.md diff --git a/resources/mddoc/reliability.md b/.github/resources/mddoc/reliability.md similarity index 100% rename from resources/mddoc/reliability.md rename to .github/resources/mddoc/reliability.md diff --git a/Cargo.toml b/Cargo.toml index 787dfd1..f634593 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Bavfalcon9 "] edition = "2021" [features] -# default = [ "async_std" ] -default = ["async_tokio" ] +default = [ "async_std" ] +# default = ["async_tokio" ] mcpe = [] debug = [] debug_all = [] diff --git a/src/client/handshake.rs b/src/client/handshake.rs index d4b5269..ec2fb77 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -10,8 +10,9 @@ use async_std::{ task::{self, Context, Poll, Waker}, }; -use binary_util::ByteReader; -use binary_util::Streamable; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::ByteReader; + #[cfg(feature = "async_tokio")] use std::future::Future; #[cfg(feature = "async_tokio")] @@ -26,13 +27,13 @@ use tokio::{ use crate::connection::queue::send::SendQueue; use crate::connection::queue::RecvQueue; use crate::protocol::frame::FramePacket; -use crate::protocol::packet::RakPacket; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, SessionInfoRequest, }; use crate::protocol::packet::online::ConnectedPong; use crate::protocol::packet::online::{ConnectionRequest, NewConnection, OnlinePacket}; +use crate::protocol::packet::RakPacket; use crate::protocol::reliability::Reliability; use crate::protocol::Magic; use crate::rakrs_debug; @@ -112,7 +113,8 @@ macro_rules! expect_reply { // rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); - if let Ok(packet) = <$reply>::compose(&mut recv_buf[1..len], &mut 0) { + let mut reader = ByteReader::from(&recv_buf[1..len]); + if let Ok(packet) = <$reply>::read(&mut reader) { pk = Some(packet); break; } else { @@ -196,21 +198,23 @@ impl ClientHandshake { let reply = match_ids!( socket.clone(), - OpenConnectReply::id(), - IncompatibleProtocolVersion::id() + // Open connect Reply + 0x06, + // Incompatible protocol version + 0x19 ); if reply.is_none() { update_state!(true, shared_state, HandshakeStatus::Failed); } - if let Ok(_) = - IncompatibleProtocolVersion::compose(&mut reply.clone().unwrap()[1..], &mut 0) - { + if let Ok(_) = IncompatibleProtocolVersion::read(&mut ByteReader::from( + &reply.clone().unwrap()[1..], + )) { update_state!(true, shared_state, HandshakeStatus::IncompatibleVersion); } - let open_reply = OpenConnectReply::compose(&mut reply.unwrap()[1..], &mut 0); + let open_reply = OpenConnectReply::read(&mut ByteReader::from(&reply.unwrap()[1..])); if open_reply.is_err() { let mut state = shared_state.lock().unwrap(); @@ -291,16 +295,18 @@ impl ClientHandshake { Ok((l, _)) => len = l, }; + let mut reader = ByteReader::from(&buf[..len]); + // proccess frame packet match buf[0] { 0x80..=0x8d => { - if let Ok(pk) = FramePacket::read(&mut buf[..len]) { + if let Ok(pk) = FramePacket::read(&mut reader) { recv_q.insert(pk).unwrap(); let raw_packets = recv_q.flush(); - for mut raw_pk in raw_packets { - let pk = ByteReader::from(&mut raw_pk[..]); + for raw_pk in raw_packets { + let mut pk = ByteReader::from(&raw_pk[..]); if let Ok(pk) = OnlinePacket::read(&mut pk) { match pk { @@ -402,19 +408,21 @@ impl Future for ClientHandshake { } async fn send_packet(socket: &Arc, packet: RakPacket) -> bool { - if let Err(e) = socket - .send_to( - &mut packet.parse().unwrap()[..], - socket.peer_addr().unwrap(), - ) - .await - { - rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); - rakrs_debug!(true, " -> PAYLOAD: {:?}", &packet.parse().unwrap()[..]); - rakrs_debug!(true, " -> PACKET: {:?}", packet); - return false; + if let Ok(buf) = packet.write_to_bytes() { + if let Err(e) = socket + .send_to(buf.as_slice(), socket.peer_addr().unwrap()) + .await + { + rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); + rakrs_debug!(true, " -> PAYLOAD: {:?}", buf); + rakrs_debug!(true, " -> PACKET: {:?}", packet); + return false; + } else { + rakrs_debug!(true, "[CLIENT] Sent payload to server!"); + return true; + } } else { - rakrs_debug!(true, "[CLIENT] Sent payload to server!"); - return true; + rakrs_debug!("[CLIENT] Failed writing payload to bytes!"); + return false; } } diff --git a/src/client/mod.rs b/src/client/mod.rs index cf54c03..7c77712 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -19,6 +19,7 @@ use async_std::{ use futures::{select, FutureExt}; use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::ByteReader; #[cfg(feature = "async_tokio")] use tokio::{ @@ -287,12 +288,7 @@ impl Client { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q - .insert( - buffer.to_vec(), - Reliability::ReliableOrd, - false, - Some(channel), - ) + .insert(buffer, Reliability::ReliableOrd, false, Some(channel)) .await { rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); @@ -313,12 +309,7 @@ impl Client { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q - .insert( - buffer.to_vec(), - Reliability::ReliableSeq, - false, - Some(channel), - ) + .insert(buffer, Reliability::ReliableSeq, false, Some(channel)) .await { rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); @@ -339,7 +330,7 @@ impl Client { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q - .insert(buffer.to_vec(), reliability, false, Some(channel)) + .insert(buffer, reliability, false, Some(channel)) .await { rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); @@ -360,7 +351,7 @@ impl Client { if self.state.lock().await.is_available() { let mut send_q = self.send_queue.as_ref().unwrap().write().await; if let Err(send) = send_q - .insert(buffer.to_vec(), reliability, true, Some(channel)) + .insert(buffer, reliability, true, Some(channel)) .await { rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); @@ -378,16 +369,16 @@ impl Client { // Flush the queue of acks and nacks, and respond to them let ack = Ack::from_records(recv_q.ack_flush(), false); if ack.records.len() > 0 { - if let Ok(p) = ack.parse() { - send_q.send_stream(&p).await; + if let Ok(p) = ack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; } } // flush nacks from recv queue let nack = Ack::from_records(recv_q.nack_queue(), true); if nack.records.len() > 0 { - if let Ok(p) = nack.parse() { - send_q.send_stream(&p).await; + if let Ok(p) = nack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; } } } @@ -419,7 +410,12 @@ impl Client { }; if let Err(_) = socket - .send(RakPacket::Offline(OfflinePacket::UnconnectedPing(unconnected_ping))) + .send( + RakPacket::from(unconnected_ping) + .write_to_bytes() + .unwrap() + .as_slice(), + ) .await { rakrs_debug!(true, "[CLIENT] Failed to send ping packet!"); @@ -431,15 +427,18 @@ impl Client { if let Ok(recvd) = timeout(Duration::from_millis(10000), socket.recv(&mut buf)).await { match recvd { Ok(l) => { - let packet = OfflinePacket::read(&mut buf[..l]).unwrap(); - if packet.is_offline() { - match packet.get_offline() { - OfflinePacket::UnconnectedPong(pk) => { + let mut reader = ByteReader::from(&buf[..l]); + let packet = RakPacket::read(&mut reader).unwrap(); + + match packet { + RakPacket::Offline(offline) => match offline { + OfflinePacket::UnconnectedPong(pong) => { rakrs_debug!(true, "[CLIENT] Recieved pong packet!"); - return Ok(pk); + return Ok(pong); } _ => {} - }; + }, + _ => {} } } Err(_) => { @@ -499,7 +498,7 @@ impl Client { #[cfg(feature = "async_tokio")] if let None = $pk_recv { - rakrs_debug!(true, "[CLIENT] (recv_task)Failed to recieve anything on netowrk channel, is there a sender?"); + rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); continue; } @@ -520,11 +519,11 @@ impl Client { // drop here so the lock isn't held for too long drop(client_state); - let mut buffer = $pk_recv.unwrap(); + let mut buffer = ByteReader::from($pk_recv.unwrap()); - match buffer[0] { + match buffer.as_slice()[0] { 0x80..=0x8d => { - if let Ok(frame_packet) = FramePacket::compose(&mut buffer[..], &mut 0) { + if let Ok(frame_packet) = FramePacket::read(&mut buffer) { let mut recv_q = recv_queue.lock().await; if let Err(_) = recv_q.insert(frame_packet) { rakrs_debug!( @@ -535,64 +534,67 @@ impl Client { let buffers = recv_q.flush(); - 'buf_loop: for mut pk_buf in buffers { - if let Ok(packet) = RakPacket::read(&mut pk_buf[..], &mut 0) { - if packet.is_online() { - match packet.get_online() { - OnlinePacket::ConnectedPing(pk) => { - let response = ConnectedPong { - ping_time: pk.time, - pong_time: current_epoch() as i64, - }; - let mut q = send_queue.write().await; - if let Err(_) = q - .send_packet( - response.into(), - Reliability::Unreliable, + 'buf_loop: for pk_buf_raw in buffers { + let mut pk_buf = ByteReader::from(&pk_buf_raw[..]); + if let Ok(rak_packet) = RakPacket::read(&mut pk_buf) { + match rak_packet { + RakPacket::Online(pk) => { + match pk { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_queue.write().await; + if let Err(_) = q + .send_packet( + response.into(), + Reliability::Unreliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + continue 'buf_loop; + } + OnlinePacket::ConnectedPong(_) => { + // todo: add ping time to client + rakrs_debug!( true, - ) - .await - { + "[CLIENT] Recieved pong packet!" + ); + } + OnlinePacket::Disconnect(_) => { rakrs_debug!( true, - "[CLIENT] Failed to send pong packet!" + "[CLIENT] Recieved disconnect packet!" ); + break 'task_loop; } - continue 'buf_loop; - } - OnlinePacket::ConnectedPong(_) => { - // todo: add ping time to client - rakrs_debug!( - true, - "[CLIENT] Recieved pong packet!" - ); - } - OnlinePacket::Disconnect(_) => { - rakrs_debug!( - true, - "[CLIENT] Recieved disconnect packet!" - ); - break 'task_loop; - } - _ => {} - }; - - rakrs_debug!( - true, - "[CLIENT] Processing fault packet... {:#?}", - packet - ); + _ => { + rakrs_debug!( + true, + "[CLIENT] Processing fault packet... {:#?}", + pk + ); - if let Err(_) = internal_sender.send(pk_buf).await { - rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + if let Err(_) = internal_sender.send(pk_buf_raw).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } + }, + RakPacket::Offline(_) => { + rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); } - } else { - // we should never recieve an offline packet after the handshake - rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); } } else { // we send this packet - if let Err(_) = internal_sender.send(pk_buf).await { + if let Err(_) = internal_sender.send(pk_buf_raw).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } } @@ -600,15 +602,15 @@ impl Client { } } NACK => { - if let Ok(nack) = Ack::compose(&mut buffer[..], &mut 0) { + if let Ok(nack) = Ack::read(&mut buffer) { let mut send_q = send_queue.write().await; let to_resend = send_q.nack(nack); if to_resend.len() > 0 { for ack_packet in to_resend { - if let Ok(buffer) = ack_packet.parse() { + if let Ok(buffer) = ack_packet.write_to_bytes() { if let Err(_) = send_q - .insert(buffer, Reliability::Unreliable, true, Some(0)) + .insert(buffer.as_slice(), Reliability::Unreliable, true, Some(0)) .await { rakrs_debug!( @@ -627,7 +629,7 @@ impl Client { } } ACK => { - if let Ok(ack) = Ack::compose(&mut buffer[..], &mut 0) { + if let Ok(ack) = Ack::read(&mut buffer) { let mut send_q = send_queue.write().await; send_q.ack(ack.clone()); @@ -639,7 +641,7 @@ impl Client { _ => { // we don't know what this is, so we're going to send it to the user, maybe // this is a custom packet - if let Err(_) = internal_sender.send(buffer).await { + if let Err(_) = internal_sender.send(buffer.as_slice().to_vec()).await { rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); } } @@ -757,16 +759,16 @@ impl Client { // Flush the queue of acks and nacks, and respond to them let ack = Ack::from_records(recv_q.ack_flush(), false); if ack.records.len() > 0 { - if let Ok(p) = ack.parse() { - send_q.send_stream(&p).await; + if let Ok(p) = ack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; } } // flush nacks from recv queue let nack = Ack::from_records(recv_q.nack_queue(), true); if nack.records.len() > 0 { - if let Ok(p) = nack.parse() { - send_q.send_stream(&p).await; + if let Ok(p) = nack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; } } }; diff --git a/src/connection/mod.rs b/src/connection/mod.rs index c79fd6c..6819257 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -223,16 +223,16 @@ impl Connection { // Flush the queue of acks and nacks, and respond to them let ack = Ack::from_records(recv_q.ack_flush(), false); if ack.records.len() > 0 { - if let Ok(p) = ack.parse() { - sendq.send_stream(&p).await; + if let Ok(p) = ack.write_to_bytes() { + sendq.send_stream(p.as_slice()).await; } } // flush nacks from recv queue let nack = Ack::from_records(recv_q.nack_queue(), true); if nack.records.len() > 0 { - if let Ok(p) = nack.parse() { - sendq.send_stream(&p).await; + if let Ok(p) = nack.write_to_bytes() { + sendq.send_stream(p.as_slice()).await; } } }; @@ -305,20 +305,28 @@ impl Connection { drop(cstate); let id = $payload[0]; + let mut reader = ByteReader::from(&$payload[..]); match id { // This is a frame packet. // This packet will be handled by the recv_queue 0x80..=0x8d => { - if let Ok(pk) = FramePacket::compose(&$payload[..], &mut 0) { + if let Ok(pk) = FramePacket::read(&mut reader) { let mut rq = recv_q.lock().await; - if let Ok(_) = rq.insert(pk) {}; + if let Err(e) = rq.insert(pk) { + rakrs_debug!( + true, + "[{}] Failed to insert frame packet! {:?}", + to_address_token(address), + e + ); + }; let buffers = rq.flush(); for buffer in buffers { let res = Connection::process_packet( - ByteReader::from(&buffer), &address, &sender, &send_q, &state, + ByteReader::from(buffer), &address, &sender, &send_q, &state, ) .await; if let Ok(v) = res { @@ -340,11 +348,17 @@ impl Connection { } drop(rq); + } else { + rakrs_debug!( + true, + "[{}] Failed to parse frame packet!", + to_address_token(address) + ); } } NACK => { // Validate this is a nack packet - if let Ok(nack) = Ack::compose(&$payload[..], &mut 0) { + if let Ok(nack) = Ack::read(&mut reader) { // The client acknowledges it did not recieve these packets // We should resend them. let mut sq = send_q.write().await; @@ -352,8 +366,8 @@ impl Connection { if resend.len() > 0 { for packet in resend { - if let Ok(buffer) = packet.parse() { - if let Err(_) = sq.insert(buffer, Reliability::Unreliable, true, Some(0)).await { + if let Ok(buffer) = packet.write_to_bytes() { + if let Err(_) = sq.insert(buffer.as_slice(), Reliability::Unreliable, true, Some(0)).await { rakrs_debug!( true, "[{}] Failed to insert packet into send queue!", @@ -373,7 +387,7 @@ impl Connection { } ACK => { // first lets validate this is an ack packet - if let Ok(ack) = Ack::compose(&$payload[..], &mut 0) { + if let Ok(ack) = Ack::read(&mut reader) { // The client acknowledges it recieved these packets // We should remove them from the queue. let mut sq = send_q.write().await; @@ -428,13 +442,15 @@ impl Connection { } pub async fn process_packet( - buffer: ByteReader, + mut buffer: ByteReader, address: &SocketAddr, sender: &Sender>, send_q: &Arc>, state: &Arc>, ) -> Result { - if let Ok(online_packet) = OnlinePacket::read(&mut buffer) { + let raw = buffer.clone(); + let raw = raw.as_slice(); + if let Ok(online_packet) = OnlinePacket::read(&mut buffer.clone()) { match online_packet { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { @@ -479,7 +495,7 @@ impl Connection { let mut q = send_q.write().await; *state.lock().await = ConnectionState::Connecting; if let Ok(_) = q - .send_packet(response.into(), Reliability::Reliable, true) + .send_packet(response.clone().into(), Reliability::Reliable, true) .await { return Ok(false); @@ -506,14 +522,18 @@ impl Connection { *state.lock().await = ConnectionState::Connected; return Ok(false); } - _ => {} + _ => { + rakrs_debug!( + true, + "[{}] Forwarding packet to socket!\n{:?}", + to_address_token(*address), + buffer.as_slice() + ); + sender.send(raw.to_vec()).await.unwrap(); + return Ok(false); + } } - sender.send(buffer.as_slice()).await.unwrap(); - return Ok(false); } else if let Ok(_) = OfflinePacket::read(&mut buffer) { - sender.send(buffer.as_slice()).await.unwrap(); - return Ok(false); - } else { *state.lock().await = ConnectionState::Disconnecting; rakrs_debug!( true, @@ -522,6 +542,14 @@ impl Connection { ); return Err(()); } + + rakrs_debug!( + true, + "[{}] Either Game-packet or unknown packet, sending buffer to client...", + to_address_token(*address) + ); + sender.send(raw.to_vec()).await.unwrap(); + Ok(false) } /// Recieve a packet from the client. @@ -577,7 +605,7 @@ impl Connection { /// Send a packet to the client. /// These will be sent next tick unless otherwise specified. - pub async fn send(&self, buffer: Vec, immediate: bool) -> Result<(), SendQueueError> { + pub async fn send(&self, buffer: &[u8], immediate: bool) -> Result<(), SendQueueError> { let mut q = self.send_queue.write().await; if let Err(e) = q .insert(buffer, Reliability::ReliableOrd, immediate, Some(0)) diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs index dc32948..2af3f06 100644 --- a/src/connection/queue/mod.rs +++ b/src/connection/queue/mod.rs @@ -354,7 +354,7 @@ impl FragmentQueue { let mut index: u32 = 0; for buf in splits.iter() { - let mut f = Frame::new(Reliability::ReliableOrd, Some(buf.clone())); + let mut f = Frame::new(Reliability::ReliableOrd, Some(&buf[..])); f.fragment_meta = Some(FragmentMeta { index, size: splits.len() as u32, diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index 56a4765..cdfb440 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -4,7 +4,8 @@ use std::sync::Arc; #[cfg(feature = "async_std")] use async_std::net::UdpSocket; -use binary_util::Streamable; + +use binary_util::interfaces::Writer; #[cfg(feature = "async_tokio")] use tokio::net::UdpSocket; @@ -99,7 +100,7 @@ impl SendQueue { /// the buffer is larger than max MTU. pub async fn insert( &mut self, - packet: Vec, + packet: &[u8], reliability: Reliability, immediate: bool, channel: Option, @@ -159,8 +160,8 @@ impl SendQueue { } // Add this frame packet to the recovery queue. - if let Ok(p) = pk.parse() { - self.send_stream(&p[..]).await; + if let Ok(p) = pk.write_to_bytes() { + self.send_stream(p.as_slice()).await; self.ack.insert_id(pk.sequence, pk); return Ok(()); } else { @@ -225,8 +226,8 @@ impl SendQueue { self.ack.insert_id(self.reliable_seq.get(), pk.clone()); } - if let Ok(buf) = pk.parse() { - self.send_stream(&buf[..]).await; + if let Ok(buf) = pk.write_to_bytes() { + self.send_stream(buf.as_slice()).await; } } @@ -249,8 +250,11 @@ impl SendQueue { immediate: bool, ) -> Result<(), SendQueueError> { // parse the packet - if let Ok(buf) = packet.parse() { - if let Err(e) = self.insert(buf, reliability, immediate, None).await { + if let Ok(buf) = packet.write_to_bytes() { + if let Err(e) = self + .insert(buf.as_slice(), reliability, immediate, None) + .await + { rakrs_debug!( true, "[{}] Failed to insert packet into send queue: {:?}", @@ -286,8 +290,8 @@ impl SendQueue { // } for packet in resend_queue.iter() { - if let Ok(buf) = packet.parse() { - self.send_stream(&buf[..]).await; + if let Ok(buf) = packet.write_to_bytes() { + self.send_stream(buf.as_slice()).await; } } } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 5f75b27..83f0dfb 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -41,6 +41,7 @@ pub struct RangeRecord { pub end: u32, } +#[allow(dead_code)] impl RangeRecord { /// Fixes the end of the range if it is lower than the start. pub fn fix(&mut self) { @@ -50,7 +51,7 @@ impl RangeRecord { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BinaryIo)] pub struct Ack { pub id: u8, pub count: u16, diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index ca4605c..feef8ba 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -1,6 +1,7 @@ -use std::io::{Cursor, Write}; - -use binary_util::{BinaryIo, interfaces::{Reader, Writer}}; +use binary_util::{ + interfaces::{Reader, Writer}, + BinaryIo, +}; /// The information for the given fragment. /// This is used to determine how to reassemble the frame. @@ -35,11 +36,15 @@ impl FramePacket { impl Reader for FramePacket { fn read(buf: &mut binary_util::ByteReader) -> Result { + // FRAME PACKET HEADER + buf.read_u8()?; let mut frames: Vec = Vec::new(); + let sequence = buf.read_u24_le()?; loop { - if let Ok(frame) = buf.read_struct::() { + let frame_pos = buf.read_struct::(); + if let Ok(frame) = frame_pos { frames.push(frame); } else { break; @@ -117,10 +122,10 @@ impl Frame { } /// Initializes a new frame with the given reliability. - pub fn new(reliability: Reliability, body: Option>) -> Self { + pub fn new(reliability: Reliability, body: Option<&[u8]>) -> Self { Self { flags: 0, - size: if let Some(b) = body.as_ref() { + size: if let Some(b) = body { b.len() as u16 } else { 0 @@ -131,7 +136,7 @@ impl Frame { order_channel: None, fragment_meta: None, reliability, - body: body.unwrap_or(Vec::new()), + body: body.unwrap_or(&[]).to_vec(), } } @@ -152,7 +157,12 @@ impl Reader for Frame { frame.flags = buf.read_u8()?; frame.reliability = Reliability::from_flags(frame.flags); - frame.size = buf.read_u16()? / 8; + + let size = buf.read_u16(); + + if let Ok(size) = size { + frame.size = size / 8; + } if frame.reliability.is_reliable() { frame.reliable_index = Some(buf.read_u24_le()?); @@ -167,13 +177,15 @@ impl Reader for Frame { frame.order_channel = Some(buf.read_u8()?); } - if frame.flags & 0x10 == 0x10 { + if (frame.flags & 0x10) > 0 { frame.fragment_meta = Some(FragmentMeta::read(buf)?); } - let body = [0; frame.size]; + let mut body = vec![0; frame.size as usize]; - frame.body = buf.read(&)?; + if let Ok(_) = buf.read(&mut body) { + frame.body = body.to_vec(); + } Ok(frame) } @@ -181,7 +193,14 @@ impl Reader for Frame { impl Writer for Frame { fn write(&self, buf: &mut binary_util::ByteWriter) -> Result<(), std::io::Error> { - buf.write_u8(self.flags)?; + let mut flags = self.reliability.to_flags(); + + // check whether or not this frame is fragmented, if it is, set the fragment flag + if self.fragment_meta.is_some() { + flags |= 0x10; + } + + buf.write_u8(flags)?; buf.write_u16(self.size * 8)?; if self.reliability.is_reliable() { @@ -197,8 +216,14 @@ impl Writer for Frame { buf.write_u8(self.order_channel.unwrap_or(0))?; } - if self.flags & 0x10 == 0x10 { - self.fragment_meta.as_ref().unwrap().write(buf)?; + if self.fragment_meta.is_some() { + buf.write( + self.fragment_meta + .as_ref() + .unwrap() + .write_to_bytes()? + .as_slice(), + )?; } buf.write(&self.body)?; diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index 865bb1c..6860075 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -189,7 +189,7 @@ impl Reader for Motd { player_count: player_count.parse().unwrap(), player_max: player_max.parse().unwrap(), server_guid: server_guid.parse().unwrap(), - gamemode: match gamemode + gamemode: match gamemode .as_str() .parse::() .expect("Gamemode is not a byte") diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs index bffd5b7..87ee62c 100644 --- a/src/protocol/packet/mod.rs +++ b/src/protocol/packet/mod.rs @@ -68,3 +68,106 @@ impl Reader for RakPacket { )) } } + +impl From for RakPacket { + fn from(packet: OfflinePacket) -> Self { + RakPacket::Offline(packet) + } +} + +impl From for RakPacket { + fn from(packet: OnlinePacket) -> Self { + RakPacket::Online(packet) + } +} + +impl From for OnlinePacket { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::Online(packet) => packet, + _ => panic!("Invalid packet conversion"), + } + } +} + +impl From for OfflinePacket { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::Offline(packet) => packet, + _ => panic!("Invalid packet conversion"), + } + } +} + +/// A utility macro that adds the implementation for any `OnlinePacket(Pk)` where +/// `Pk` can be converted to `RakPacket`, `OnlinePacket` or `OfflinePacket` +/// and vice versa. +/// +/// For example, we want unconnected pong to be unwrapped, we can do this without +/// a match statement like this: +/// ```rust ignore +/// use raknet::packet::RakPacket; +/// use raknet::packet::online::OnlinePacket; +/// use raknet::packet::online::UnconnectedPing; +/// let some_packet = RakPacket::from(&source)?; +/// let connected_ping: UnconnectedPing = some_packet.into(); +/// ``` +/// +/// This macro also allows for converting any `OnlinePacket(Pk)` to a `RakPacket`, where `Pk` can +/// be directly converted into a packet. For example: +/// ```rust ignore +/// use raknet::packet::Packet; +/// use raknet::packet::online::OnlinePacket; +/// use raknet::packet::online::UnconnectedPong; +/// +/// let packet: Packet = UnconnectedPong { +/// magic: Magic::new(), +/// timestamp: SystemTime::now(), +/// client_id: -129 +/// }.into(); +/// ``` +/// +/// The macro can be expressed in the following way: +/// ```rust ignore +/// register_packets! { +/// Online is OnlinePacket, +/// UnconnectedPing, +/// // etc... +/// } +/// ``` +#[macro_export] +macro_rules! register_packets { + ($name: ident is $kind: ident, $($packet: ident),*) => { + $( + impl From<$packet> for $kind { + fn from(packet: $packet) -> Self { + $kind::$packet(packet) + } + } + + impl From<$packet> for RakPacket { + fn from(packet: $packet) -> Self { + $kind::$packet(packet).into() + } + } + + impl From<$kind> for $packet { + fn from(packet: $kind) -> Self { + match packet { + $kind::$packet(packet) => packet.into(), + _ => panic!("Invalid packet conversion"), + } + } + } + + impl From for $packet { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::$name(packet) => packet.into(), + _ => panic!("Invalid packet conversion"), + } + } + } + )* + }; +} diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 1d3bebd..3695207 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -1,9 +1,12 @@ use std::net::SocketAddr; +use super::RakPacket; #[cfg(feature = "mcpe")] pub use crate::protocol::mcpe::UnconnectedPong; use crate::protocol::Magic; use crate::protocol::RAKNET_HEADER_OVERHEAD; +use crate::register_packets; + use binary_util::interfaces::{Reader, Writer}; use binary_util::io::{ByteReader, ByteWriter}; use binary_util::BinaryIo; @@ -13,14 +16,25 @@ use binary_util::BinaryIo; #[repr(u8)] pub enum OfflinePacket { UnconnectedPing(UnconnectedPing) = 0x01, + UnconnectedPong(UnconnectedPong) = 0x1c, OpenConnectRequest(OpenConnectRequest) = 0x05, OpenConnectReply(OpenConnectReply) = 0x06, SessionInfoRequest(SessionInfoRequest) = 0x07, SessionInfoReply(SessionInfoReply) = 0x08, - UnconnectedPong(UnconnectedPong) = 0x1c, IncompatibleProtocolVersion(IncompatibleProtocolVersion) = 0x19, } +register_packets! { + Offline is OfflinePacket, + UnconnectedPing, + UnconnectedPong, + OpenConnectRequest, + OpenConnectReply, + SessionInfoRequest, + SessionInfoReply, + IncompatibleProtocolVersion +} + /// Unconnected Ping #[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPing { @@ -58,7 +72,7 @@ impl Reader for OpenConnectRequest { impl Writer for OpenConnectRequest { fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { - buf.write_struct::()?; + buf.write_type::(&Magic::new())?; buf.write_u8(self.protocol)?; // padding // remove 28 bytes from the mtu size diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index 0755ac5..10c3832 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -1,5 +1,8 @@ use std::net::SocketAddr; +use super::RakPacket; +use crate::register_packets; + use binary_util::interfaces::{Reader, Writer}; use binary_util::io::{ByteReader, ByteWriter}; use binary_util::BinaryIo; @@ -16,6 +19,17 @@ pub enum OnlinePacket { Disconnect(Disconnect) = 0x15, } +register_packets! { + Online is OnlinePacket, + ConnectedPing, + ConnectedPong, + LostConnection, + ConnectionRequest, + ConnectionAccept, + NewConnection, + Disconnect +} + /// Connected Ping Packet /// This packet is sent by the client to the server. /// The server should respond with a `ConnectedPong` packet. @@ -65,11 +79,11 @@ impl Reader for ConnectionAccept { // read the system index, this is let system_index = buf.read_i16()?; - let internal_ids = Vec::new::(); + let mut internal_ids = Vec::::new(); for _ in 0..20 { // we only have the request time and timestamp left... - if buf.len() < 16 { + if buf.as_slice().len() < 16 { break; } internal_ids.push(buf.read_struct::()?); @@ -90,7 +104,7 @@ impl Reader for ConnectionAccept { impl Writer for ConnectionAccept { fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { - buf.write_struct(&self.client_address)?; + buf.write_type::(&self.client_address)?; buf.write_i16(self.system_index)?; if self.internal_ids.len() > 20 { @@ -101,7 +115,7 @@ impl Writer for ConnectionAccept { } for internal_id in &self.internal_ids { - buf.write_struct(internal_id)?; + buf.write_type::(internal_id)?; } buf.write_i64(self.request_time)?; @@ -129,11 +143,11 @@ impl Reader for NewConnection { fn read(buf: &mut ByteReader) -> std::io::Result { let server_address = buf.read_struct::()?; - let system_address = Vec::new::(); + let mut system_address = Vec::::new(); for _ in 0..20 { // we only have the request time and timestamp left... - if buf.len() < 16 { + if buf.as_slice().len() < 16 { break; } system_address.push(buf.read_struct::()?); @@ -153,7 +167,7 @@ impl Reader for NewConnection { impl Writer for NewConnection { fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { - buf.write_struct(&self.server_address)?; + buf.write_type::(&self.server_address)?; if self.system_address.len() > 20 { return Err(std::io::Error::new( @@ -163,7 +177,7 @@ impl Writer for NewConnection { } for system_address in &self.system_address { - buf.write_struct(system_address)?; + buf.write_type::(system_address)?; } buf.write_i64(self.request_time)?; From a5f580d8ec0ba0438a655c0e0b54dedd9e5b4a22 Mon Sep 17 00:00:00 2001 From: john-bv Date: Sat, 12 Aug 2023 20:47:12 -0500 Subject: [PATCH 70/75] chore: only debug this error, cause the reciever may not be listening (for whatever reason) --- src/connection/mod.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 6819257..d075ae0 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -529,7 +529,13 @@ impl Connection { to_address_token(*address), buffer.as_slice() ); - sender.send(raw.to_vec()).await.unwrap(); + if let Err(_) = sender.send(raw.to_vec()).await { + rakrs_debug!( + "[{}] Failed to to forward packet to recv channel...", + to_address_token(*address) + ); + return Err(()); + } return Ok(false); } } @@ -548,7 +554,13 @@ impl Connection { "[{}] Either Game-packet or unknown packet, sending buffer to client...", to_address_token(*address) ); - sender.send(raw.to_vec()).await.unwrap(); + if let Err(_) = sender.send(raw.to_vec()).await { + rakrs_debug!( + "[{}] Failed to to forward packet to recv channel...", + to_address_token(*address) + ); + return Err(()); + } Ok(false) } From 63e7e2cacd251178be132f71b121039fce757a64 Mon Sep 17 00:00:00 2001 From: john-bv Date: Sun, 13 Aug 2023 18:09:18 -0500 Subject: [PATCH 71/75] bug(server killing): Server will now kill clients properly --- Cargo.toml | 2 +- examples/async-std/client/Cargo.toml | 2 +- src/client/handshake.rs | 4 +- src/client/mod.rs | 1 + src/connection/mod.rs | 68 ++-- src/connection/queue/recv.rs | 2 +- src/connection/queue/send.rs | 8 +- src/protocol/ack.rs | 60 ++-- src/protocol/frame.rs | 2 +- src/protocol/packet/offline.rs | 2 +- src/protocol/packet/online.rs | 8 +- src/server/mod.rs | 481 +++++++++++++++------------ 12 files changed, 360 insertions(+), 280 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f634593..40d7562 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary-util = "0.3.0" +binary-util = "0.3.4" tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml index a56f51f..5a0019f 100644 --- a/examples/async-std/client/Cargo.toml +++ b/examples/async-std/client/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } -binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } +binary-util = "0.3.0" rak-rs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file diff --git a/src/client/handshake.rs b/src/client/handshake.rs index ec2fb77..20cf0e3 100644 --- a/src/client/handshake.rs +++ b/src/client/handshake.rs @@ -53,7 +53,7 @@ macro_rules! match_ids { } let len: usize; - let send_result = timeout(Duration::from_secs(5), $socket.recv(&mut recv_buf)).await; + let send_result = timeout(Duration::from_secs(2), $socket.recv(&mut recv_buf)).await; if (send_result.is_err()) { rakrs_debug!(true, "[CLIENT] Failed to receive packet from server! Is it offline?"); @@ -93,7 +93,7 @@ macro_rules! expect_reply { } let len: usize; - let send_result = timeout(Duration::from_secs(10), $socket.recv(&mut recv_buf)).await; + let send_result = timeout(Duration::from_secs(4), $socket.recv(&mut recv_buf)).await; if (send_result.is_err()) { rakrs_debug!( diff --git a/src/client/mod.rs b/src/client/mod.rs index 7c77712..b248fa4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,3 +1,4 @@ +pub mod discovery; pub mod handshake; use std::{ diff --git a/src/connection/mod.rs b/src/connection/mod.rs index d075ae0..d9ab012 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -10,7 +10,6 @@ use std::{ }; use binary_util::interfaces::{Reader, Writer}; -use binary_util::ByteReader; #[cfg(feature = "async_std")] use async_std::{ @@ -118,6 +117,7 @@ impl Connection { address: SocketAddr, socket: &Arc, net: Receiver>, + notifier: Arc>, mtu: u16, ) -> Self { let (net_sender, net_receiver) = bounded::>(100); @@ -144,14 +144,14 @@ impl Connection { let tk = c.tasks.clone(); let mut tasks = tk.lock().await; - tasks.push(c.init_tick()); + tasks.push(c.init_tick(notifier)); tasks.push(c.init_net_recv(net, net_sender).await); return c; } /// Initializes the client ticking process! - pub fn init_tick(&self) -> task::JoinHandle<()> { + pub fn init_tick(&self, notifier: Arc>) -> task::JoinHandle<()> { let address = self.address; let closer = self.disconnect.clone(); let last_recv = self.recv_time.clone(); @@ -241,7 +241,7 @@ impl Connection { #[cfg(feature = "async_std")] select! { _ = closer.wait().fuse() => { - rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); + rakrs_debug!(true, "[{}] [task: tick] Connection has been closed due to closer!", to_address_token(address)); break; } _ = sleep(Duration::from_millis(50)).fuse() => { @@ -252,7 +252,7 @@ impl Connection { #[cfg(feature = "async_tokio")] select! { _ = closer.wait() => { - rakrs_debug!(true, "[{}] [TICK TASK] Connection has been closed due to closer!", to_address_token(address)); + rakrs_debug!(true, "[{}] [task: tick] Connection has been closed due to closer!", to_address_token(address)); break; } _ = sleep(Duration::from_millis(50)) => { @@ -261,9 +261,38 @@ impl Connection { } } + #[cfg(feature = "async_std")] + if let Ok(_) = notifier.send(address).await { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } else { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } + + #[cfg(feature = "async_tokio")] + if let Ok(_) = notifier.send(address).await { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } else { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } rakrs_debug!( true, - "[{}] Connection has been closed due to end of tick!", + "[{}] Connection has been cleaned up!", to_address_token(address) ); }); @@ -305,12 +334,11 @@ impl Connection { drop(cstate); let id = $payload[0]; - let mut reader = ByteReader::from(&$payload[..]); match id { // This is a frame packet. // This packet will be handled by the recv_queue 0x80..=0x8d => { - if let Ok(pk) = FramePacket::read(&mut reader) { + if let Ok(pk) = FramePacket::read_from_slice(&$payload[..]) { let mut rq = recv_q.lock().await; if let Err(e) = rq.insert(pk) { @@ -326,7 +354,7 @@ impl Connection { for buffer in buffers { let res = Connection::process_packet( - ByteReader::from(buffer), &address, &sender, &send_q, &state, + &buffer, &address, &sender, &send_q, &state, ) .await; if let Ok(v) = res { @@ -358,7 +386,7 @@ impl Connection { } NACK => { // Validate this is a nack packet - if let Ok(nack) = Ack::read(&mut reader) { + if let Ok(nack) = Ack::read_from_slice(&$payload[..]) { // The client acknowledges it did not recieve these packets // We should resend them. let mut sq = send_q.write().await; @@ -387,7 +415,7 @@ impl Connection { } ACK => { // first lets validate this is an ack packet - if let Ok(ack) = Ack::read(&mut reader) { + if let Ok(ack) = Ack::read_from_slice(&$payload[..]) { // The client acknowledges it recieved these packets // We should remove them from the queue. let mut sq = send_q.write().await; @@ -409,7 +437,7 @@ impl Connection { #[cfg(feature = "async_std")] select! { _ = disconnect.wait().fuse() => { - rakrs_debug!(true, "[{}] [RECV TASK] Connection has been closed due to closer!", to_address_token(address)); + rakrs_debug!(true, "[{}] [task: net_recv] Connection has been closed due to closer!", to_address_token(address)); break; } res = net.recv().fuse() => { @@ -425,7 +453,7 @@ impl Connection { #[cfg(feature = "async_tokio")] select! { _ = disconnect.wait() => { - rakrs_debug!(true, "[{}] [RECV TASK] Connection has been closed due to closer!", to_address_token(address)); + rakrs_debug!(true, "[{}] [task: net_recv] Connection has been closed due to closer!", to_address_token(address)); break; } res = net.recv() => { @@ -442,15 +470,13 @@ impl Connection { } pub async fn process_packet( - mut buffer: ByteReader, + buffer: &[u8], address: &SocketAddr, sender: &Sender>, send_q: &Arc>, state: &Arc>, ) -> Result { - let raw = buffer.clone(); - let raw = raw.as_slice(); - if let Ok(online_packet) = OnlinePacket::read(&mut buffer.clone()) { + if let Ok(online_packet) = OnlinePacket::read_from_slice(&buffer) { match online_packet { OnlinePacket::ConnectedPing(pk) => { let response = ConnectedPong { @@ -527,9 +553,9 @@ impl Connection { true, "[{}] Forwarding packet to socket!\n{:?}", to_address_token(*address), - buffer.as_slice() + buffer ); - if let Err(_) = sender.send(raw.to_vec()).await { + if let Err(_) = sender.send(buffer.to_vec()).await { rakrs_debug!( "[{}] Failed to to forward packet to recv channel...", to_address_token(*address) @@ -539,7 +565,7 @@ impl Connection { return Ok(false); } } - } else if let Ok(_) = OfflinePacket::read(&mut buffer) { + } else if let Ok(_) = OfflinePacket::read_from_slice(&buffer) { *state.lock().await = ConnectionState::Disconnecting; rakrs_debug!( true, @@ -554,7 +580,7 @@ impl Connection { "[{}] Either Game-packet or unknown packet, sending buffer to client...", to_address_token(*address) ); - if let Err(_) = sender.send(raw.to_vec()).await { + if let Err(_) = sender.send(buffer.to_vec()).await { rakrs_debug!( "[{}] Failed to to forward packet to recv channel...", to_address_token(*address) diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs index 48d5878..acb46ed 100644 --- a/src/connection/queue/recv.rs +++ b/src/connection/queue/recv.rs @@ -143,7 +143,7 @@ impl Ackable for RecvQueue { self.nack.remove(&sequence); } Record::Range(ranged) => { - for i in ranged.start..ranged.end { + for i in *ranged.start.0..*ranged.end.0 { self.nack.remove(&i); } } diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs index cdfb440..eb98fc7 100644 --- a/src/connection/queue/send.rs +++ b/src/connection/queue/send.rs @@ -309,10 +309,10 @@ impl Ackable for SendQueue { for record in ack.records.iter() { match record { Record::Single(SingleRecord { sequence }) => { - if let Ok(_) = self.ack.remove(*sequence) {}; + if let Ok(_) = self.ack.remove(*sequence.0) {}; } Record::Range(ranged) => { - for i in ranged.start..ranged.end { + for i in *ranged.start.0..*ranged.end.0 { if let Ok(_) = self.ack.remove(i) {}; } } @@ -331,12 +331,12 @@ impl Ackable for SendQueue { for record in nack.records.iter() { match record { Record::Single(single) => { - if let Ok(packet) = self.ack.get(single.sequence) { + if let Ok(packet) = self.ack.get(*single.sequence.0) { resend_queue.push(packet.clone()); } } Record::Range(ranged) => { - for i in ranged.start..ranged.end { + for i in *ranged.start.0..*ranged.end.0 { if let Ok(packet) = self.ack.get(i) { resend_queue.push(packet.clone()); } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 83f0dfb..5352111 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -3,7 +3,7 @@ pub const NACK: u8 = 0xa0; use std::ops::Range; -use binary_util::BinaryIo; +use binary_util::{BinaryIo, types::{u24, LE}}; pub(crate) trait Ackable { type NackItem; @@ -32,13 +32,13 @@ pub enum Record { #[derive(Debug, Clone, BinaryIo)] pub struct SingleRecord { - pub sequence: u32, + pub sequence: LE, } #[derive(Debug, Clone, BinaryIo)] pub struct RangeRecord { - pub start: u32, - pub end: u32, + pub start: LE, + pub end: LE, } #[allow(dead_code)] @@ -76,42 +76,36 @@ impl Ack { let mut current: Range = 0..0; missing.sort(); - for m in missing { - // if the current range is empty, set the start to the current missing - if m > current.start && current.start == 0 { + for m in missing.clone() { + if current.start == 0 { current.start = m; current.end = m; - continue; - } - if m == current.end + 1 { + } else if m == current.end + 1 { current.end = m; - continue; } else { // end of range - records.push(Record::Range(RangeRecord { - start: current.start, - end: current.end, - })); - current.start = 0; - current.end = 0; - - // we also need to add the current missing to the records - records.push(Record::Single(SingleRecord { sequence: m })); - } - } + if current.start == current.end { + records.push( + Record::Single( + SingleRecord { + sequence: LE(current.start.into()), + } + ) + ); + } else { + records.push( + Record::Range( + RangeRecord { + start: LE(current.start.into()), + end: LE(current.end.into()), + } + ) + ); + } - if current.start == current.end { - if current.start == 0 { - return Self::new(0, false); + current.start = m; + current.end = m; } - records.push(Record::Single(SingleRecord { - sequence: current.start, - })); - } else if current.start != 0 && current.end != 0 { - records.push(Record::Range(RangeRecord { - start: current.start, - end: current.end, - })); } let mut nack = Self::new(records.len().try_into().unwrap(), nack); diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs index feef8ba..bd16a3f 100644 --- a/src/protocol/frame.rs +++ b/src/protocol/frame.rs @@ -43,7 +43,7 @@ impl Reader for FramePacket { let sequence = buf.read_u24_le()?; loop { - let frame_pos = buf.read_struct::(); + let frame_pos = buf.read_type::(); if let Ok(frame) = frame_pos { frames.push(frame); } else { diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 3695207..967fbe8 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -62,7 +62,7 @@ pub struct OpenConnectRequest { impl Reader for OpenConnectRequest { fn read(buf: &mut ByteReader) -> Result { let len = buf.as_slice().len(); - buf.read_struct::()?; + buf.read_type::()?; Ok(OpenConnectRequest { protocol: buf.read_u8()?, mtu_size: (len + 1 + 28) as u16, diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index 10c3832..4c8f1c2 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -75,7 +75,7 @@ pub struct ConnectionAccept { impl Reader for ConnectionAccept { fn read(buf: &mut ByteReader) -> std::io::Result { - let client_address = buf.read_struct::()?; + let client_address = buf.read_type::()?; // read the system index, this is let system_index = buf.read_i16()?; @@ -86,7 +86,7 @@ impl Reader for ConnectionAccept { if buf.as_slice().len() < 16 { break; } - internal_ids.push(buf.read_struct::()?); + internal_ids.push(buf.read_type::()?); } let request_time = buf.read_i64()?; @@ -141,7 +141,7 @@ pub struct NewConnection { impl Reader for NewConnection { fn read(buf: &mut ByteReader) -> std::io::Result { - let server_address = buf.read_struct::()?; + let server_address = buf.read_type::()?; let mut system_address = Vec::::new(); @@ -150,7 +150,7 @@ impl Reader for NewConnection { if buf.as_slice().len() < 16 { break; } - system_address.push(buf.read_struct::()?); + system_address.push(buf.read_type::()?); } let request_time = buf.read_i64()?; diff --git a/src/server/mod.rs b/src/server/mod.rs index 4e0ef7a..c0c2e9c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,7 +4,6 @@ pub mod event; use std::collections::HashMap; -use std::sync::atomic::AtomicBool; use std::{net::SocketAddr, sync::Arc}; #[cfg(feature = "async_std")] @@ -14,9 +13,12 @@ use async_std::{ sync::Mutex, task::{self}, }; -use binary_util::ByteReader; +#[cfg(feature = "async_std")] +use futures::{select, FutureExt}; +use binary_util::ByteReader; use binary_util::interfaces::{Reader, Writer}; + #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, @@ -24,10 +26,12 @@ use tokio::{ sync::mpsc::{Receiver, Sender}, sync::Mutex, task::{self}, + select }; use crate::connection::{ConnMeta, Connection}; use crate::error::server::ServerError; +use crate::notify::Notify; use crate::protocol::mcpe::motd::Motd; use crate::protocol::packet::offline::{ IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, @@ -119,7 +123,7 @@ pub struct Listener { // TODO /// A Notifier (sephamore) that will wait until all notified listeners /// are completed, and finish closing. - closed: Arc, + closed: Arc, // This is a notifier that acknowledges all connections have been removed from the server successfully. // This is important to prevent memory leaks if the process is continously running. // cleanup: Arc, @@ -167,7 +171,7 @@ impl Listener { serving: false, connections: Arc::new(Mutex::new(HashMap::new())), // closer: Arc::new(Semaphore::new(0)), - closed: Arc::new(AtomicBool::new(false)), + closed: Arc::new(Notify::new()), // cleanup: Arc::new(Notify::new()), // cleanup: Arc::new(Condvar::new()), }; @@ -199,11 +203,18 @@ impl Listener { let default_motd = self.motd.clone(); let connections = self.connections.clone(); let closer = self.closed.clone(); - // let cleanup = self.cleanup.clone(); + let connections2 = self.connections.clone(); + let closer2 = self.closed.clone(); let versions = self.versions.clone(); self.serving = true; + #[cfg(feature = "async_std")] + let (cs, client_close_recv) = bounded::(10); + #[cfg(feature = "async_tokio")] + let (cs, mut client_close_recv) = bounded::(10); + let client_close_send = Arc::new(cs); + task::spawn(async move { // We allocate here to prevent constant allocation of this array let mut buf: [u8; 2048] = [0; 2048]; @@ -211,209 +222,266 @@ impl Listener { let motd_default = default_motd.clone(); loop { - // TODO: This may be fixable by killing the task itself. - if closer.load(std::sync::atomic::Ordering::Relaxed) { - rakrs_debug!("Server is closing, stopping recieve loop"); - break; - } - let length: usize; let origin: SocketAddr; - // We need to wait for either the socket to stop recieving, - // or a server close notification. - let recv = socket.recv_from(&mut buf).await; - match recv { - Ok((l, o)) => { - length = l; - origin = o; - } - Err(e) => { - rakrs_debug!(true, "Error: {:?}", e); - continue; - } - } - - // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally - if let Ok(pk) = OfflinePacket::read(&mut ByteReader::from(&buf[..length])) { - // Offline packets are not buffered to the user. - // The reason for this is because we don't wish for the user to be able to disrupt - // raknet protocol, and handshaking. - match pk { - OfflinePacket::UnconnectedPing(_) => { - // let (resp_tx, resp_rx) = - // oneshot::channel::(); - #[cfg(feature = "mcpe")] - let motd: Motd = motd_default.clone(); - - // if let Err(e) = send_evt.try_send(( - // ServerEvent::RefreshMotdRequest(origin, motd.clone()), - // // resp_tx, - // )) - // { - // match e { - // TrySendError::Full(_) => { - // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); - // } - // TrySendError::Closed(_) => { - // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); - // } - // } - // } - - // if let Ok(res) = resp_rx.await { - // // get the motd from the server event otherwise use defaults. - // // if let Ok(res) = res { - // match res { - // ServerEventResponse::RefreshMotd(m) => { - // motd = m; - // } - // _ => { - // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); - // } - // } - // // }; - // } - - // unconnected pong signature is different if MCPE is specified. - let resp = UnconnectedPong { - timestamp: current_epoch(), - server_id, - magic: Magic::new(), - #[cfg(feature = "mcpe")] - motd, - }; - - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; - } - OfflinePacket::OpenConnectRequest(mut pk) => { - // TODO make a constant for this - if !versions.contains(&pk.protocol) { - let resp = IncompatibleProtocolVersion { - protocol: pk.protocol, - magic: Magic::new(), - server_id, - }; - - rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); - - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; + macro_rules! recv_body { + ($recv: ident) => { + match $recv { + Ok((l, o)) => { + length = l; + origin = o; } - - rakrs_debug!( - true, - "[{}] Client requested Mtu Size: {}", - to_address_token(*&origin), - pk.mtu_size - ); - - if pk.mtu_size > 2048 { - rakrs_debug!( - true, - "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", - to_address_token(*&origin), - pk.mtu_size - ); - pk.mtu_size = 2048; + Err(e) => { + rakrs_debug!(true, "Error: {:?}", e); + continue; } - - let resp = OpenConnectReply { - server_id, - // TODO allow encryption - security: false, - magic: Magic::new(), - // TODO make this configurable, this is sent to the client to change - // it's mtu size, right now we're using what the client prefers. - // however in some cases this may not be the preferred use case, for instance - // on servers with larger worlds, you may want a larger mtu size, or if - // your limited on network bandwith - mtu_size: pk.mtu_size, - }; - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; } - OfflinePacket::SessionInfoRequest(pk) => { - let resp = SessionInfoReply { - server_id, - client_address: origin, - magic: Magic::new(), - mtu_size: pk.mtu_size, - security: false, - }; - - // This is a valid packet, let's check if a session exists, if not, we should create it. - // Event if the connection is only in offline mode. - let mut sessions = connections.lock().await; - - if !sessions.contains_key(&origin) { - rakrs_debug!(true, "Creating new session for {}", origin); - let meta = ConnMeta::new(0); - let (net_send, net_recv) = bounded::>(10); - let connection = - Connection::new(origin, &socket, net_recv, pk.mtu_size).await; - rakrs_debug!(true, "Created Session for {}", origin); - - // Add the connection to the available connections list. - // we're using the name "sessions" here to differeniate - // for some reason the reciever likes to be dropped, so we're saving it here. - sessions.insert(origin, (meta, net_send)); - - // notify the connection communicator - if let Err(err) = send_comm.send(connection).await { - let connection = err.0; - // there was an error, and we should terminate this connection immediately. - rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); - sessions.remove(&origin); + + // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally + if let Ok(pk) = OfflinePacket::read(&mut ByteReader::from(&buf[..length])) { + // Offline packets are not buffered to the user. + // The reason for this is because we don't wish for the user to be able to disrupt + // raknet protocol, and handshaking. + match pk { + OfflinePacket::UnconnectedPing(_) => { + // let (resp_tx, resp_rx) = + // oneshot::channel::(); + #[cfg(feature = "mcpe")] + let motd: Motd = motd_default.clone(); + + // if let Err(e) = send_evt.try_send(( + // ServerEvent::RefreshMotdRequest(origin, motd.clone()), + // // resp_tx, + // )) + // { + // match e { + // TrySendError::Full(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); + // } + // TrySendError::Closed(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); + // } + // } + // } + + // if let Ok(res) = resp_rx.await { + // // get the motd from the server event otherwise use defaults. + // // if let Ok(res) = res { + // match res { + // ServerEventResponse::RefreshMotd(m) => { + // motd = m; + // } + // _ => { + // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); + // } + // } + // // }; + // } + + // unconnected pong signature is different if MCPE is specified. + let resp = UnconnectedPong { + timestamp: current_epoch(), + server_id, + magic: Magic::new(), + #[cfg(feature = "mcpe")] + motd, + }; + + send_packet_to_socket(&socket, resp.into(), origin).await; continue; } + OfflinePacket::OpenConnectRequest(mut pk) => { + // TODO make a constant for this + if !versions.contains(&pk.protocol) { + let resp = IncompatibleProtocolVersion { + protocol: pk.protocol, + magic: Magic::new(), + server_id, + }; + + rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {}", + to_address_token(*&origin), + pk.mtu_size + ); + + if pk.mtu_size > 2048 { + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", + to_address_token(*&origin), + pk.mtu_size + ); + pk.mtu_size = 2048; + } + + let resp = OpenConnectReply { + server_id, + // TODO allow encryption + security: false, + magic: Magic::new(), + // TODO make this configurable, this is sent to the client to change + // it's mtu size, right now we're using what the client prefers. + // however in some cases this may not be the preferred use case, for instance + // on servers with larger worlds, you may want a larger mtu size, or if + // your limited on network bandwith + mtu_size: pk.mtu_size, + }; + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + OfflinePacket::SessionInfoRequest(pk) => { + let resp = SessionInfoReply { + server_id, + client_address: origin, + magic: Magic::new(), + mtu_size: pk.mtu_size, + security: false, + }; + + // This is a valid packet, let's check if a session exists, if not, we should create it. + // Event if the connection is only in offline mode. + let mut sessions = connections.lock().await; + + if !sessions.contains_key(&origin) { + rakrs_debug!(true, "Creating new session for {}", origin); + let meta = ConnMeta::new(0); + let (net_send, net_recv) = bounded::>(10); + let connection = + Connection::new(origin, &socket, net_recv, client_close_send.clone(), pk.mtu_size).await; + rakrs_debug!(true, "Created Session for {}", origin); + + // Add the connection to the available connections list. + // we're using the name "sessions" here to differeniate + // for some reason the reciever likes to be dropped, so we're saving it here. + sessions.insert(origin, (meta, net_send)); + + // notify the connection communicator + if let Err(err) = send_comm.send(connection).await { + let connection = err.0; + // there was an error, and we should terminate this connection immediately. + rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); + sessions.remove(&origin); + continue; + } + } + + // update the sessions mtuSize, this is referred to internally, we also will send this event to the client + // event channel. However we are not expecting a response. + + sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; + rakrs_debug!( + true, + "[{}] Updated mtu size to {}", + to_address_token(origin), + pk.mtu_size + ); + + // let (resp_tx, resp_rx) = oneshot::channel::(); + + // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) + // .await + // { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + _ => { + rakrs_debug!( + "[{}] Received invalid packet!", + to_address_token(*&origin) + ); + } } + } - // update the sessions mtuSize, this is referred to internally, we also will send this event to the client - // event channel. However we are not expecting a response. - - sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; - - // let (resp_tx, resp_rx) = oneshot::channel::(); + // Packet may be valid, but we'll let the connection decide this + let mut sessions = connections.lock().await; + if sessions.contains_key(&origin) { + if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed! (Removed the connection)", to_address_token(*&origin)); + sessions.remove(&origin); + } + } + drop(sessions); + }; + } - // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { - // rakrs_debug!( - // "[{}] Failed to update mtu size with the client!", - // to_address_token(origin) - // ); - // } + #[cfg(feature = "async_std")] + select! { + _ = closer.wait().fuse() => { + rakrs_debug!(true, "[SERVER] [NETWORK] Server has recieved the shutdown notification!"); + break; + } + recv = socket.recv_from(&mut buf).fuse() => { + recv_body!(recv); + } + } - // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) - // .await - // { - // rakrs_debug!( - // "[{}] Failed to update mtu size with the client!", - // to_address_token(origin) - // ); - // } + #[cfg(feature = "async_tokio")] + select! { + _ = closer.wait() => { + rakrs_debug!(true, "[SERVER] [NETWORK] Server has recieved the shutdown notification!"); + break; + } + recv = socket.recv_from(&mut buf) => { + recv_body!(recv); + } + } + } + }); - send_packet_to_socket(&socket, resp.into(), origin).await; - continue; - } - _ => { - rakrs_debug!( - "[{}] Received invalid packet!", - to_address_token(*&origin) - ); + task::spawn(async move { + // here we loop and recv from the client_close_recv channel + // and remove the connection from the hashmap + loop { + #[cfg(feature = "async_std")] + select! { + _ = closer2.wait().fuse() => { + rakrs_debug!(true, "[SERVER] [Cleanup] Server has recieved the shutdown notification!"); + break; + } + addr = client_close_recv.recv().fuse() => { + if let Ok(addr) = addr { + rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); + connections2.lock().await.remove(&addr); } } } - // Packet may be valid, but we'll let the connection decide this - let mut sessions = connections.lock().await; - if sessions.contains_key(&origin) { - if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { - rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed! (Removed the connection)", to_address_token(*&origin)); - sessions.remove(&origin); + #[cfg(feature = "async_tokio")] + select! { + _ = closer2.wait() => { + rakrs_debug!(true, "[SERVER] [Cleanup] Server has recieved the shutdown notification!"); + break; + } + addr = client_close_recv.recv() => { + if let Some(addr) = addr { + rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); + connections2.lock().await.remove(&addr); + } } } - drop(sessions); } }); @@ -446,32 +514,23 @@ impl Listener { if !self.serving { Err(ServerError::NotListening) } else { - if self.closed.load(std::sync::atomic::Ordering::Relaxed) { - return Err(ServerError::Killed); - } else { - let receiver = self.recv_comm.recv().await; - return match receiver { - #[cfg(feature = "async_std")] - Ok(c) => Ok(c), - #[cfg(feature = "async_std")] - Err(_) => Err(ServerError::Killed), - #[cfg(feature = "async_tokio")] - Some(c) => Ok(c), - #[cfg(feature = "async_tokio")] - None => Err(ServerError::Killed), - }; - } + let receiver = self.recv_comm.recv().await; + return match receiver { + #[cfg(feature = "async_std")] + Ok(c) => Ok(c), + #[cfg(feature = "async_std")] + Err(_) => Err(ServerError::Killed), + #[cfg(feature = "async_tokio")] + Some(c) => Ok(c), + #[cfg(feature = "async_tokio")] + None => Err(ServerError::Killed), + }; } } /// Stops the listener and frees the socket. pub async fn stop(&mut self) -> Result<(), ServerError> { - if self.closed.load(std::sync::atomic::Ordering::Relaxed) { - return Ok(()); - } - - self.closed - .store(true, std::sync::atomic::Ordering::Relaxed); + self.closed.notify().await; // self.cleanup.notified().await; self.sock = None; @@ -498,5 +557,5 @@ pub(crate) fn current_epoch() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() - .as_millis() as u64 + .as_secs() as u64 } From 635ab1e2a69bc6ee7cc102a175a2718529e69a92 Mon Sep 17 00:00:00 2001 From: john-bv Date: Sun, 13 Aug 2023 18:10:08 -0500 Subject: [PATCH 72/75] feat: start mtu discovery --- src/client/discovery.rs | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/client/discovery.rs diff --git a/src/client/discovery.rs b/src/client/discovery.rs new file mode 100644 index 0000000..afa0f72 --- /dev/null +++ b/src/client/discovery.rs @@ -0,0 +1,53 @@ +#[cfg(feature = "async_std")] +use async_std::{ + future::timeout, + future::Future, + net::UdpSocket, + task::{self, Context, Poll, Waker}, +}; + +#[cfg(feature = "async_tokio")] +use std::future::Future; +use std::sync::{Arc, Mutex}; +#[cfg(feature = "async_tokio")] +use std::task::{Context, Poll, Waker}; +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + task::{self}, + time::timeout, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DiscoveryStatus { + Initiated, + Discovered, + Failed, + Undiscovered, +} + +struct DiscoveryState { + status: DiscoveryStatus, + waker: Option, +} + +pub struct MtuDiscovery { + state: Arc>, +} + +impl MtuDiscovery {} + +impl Future for MtuDiscovery { + type Output = DiscoveryStatus; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut state = self.state.lock().unwrap(); + match state.status { + DiscoveryStatus::Failed | DiscoveryStatus::Discovered => Poll::Ready(state.status), + _ => { + state.waker = Some(cx.waker().clone()); + Poll::Pending + } + } + } +} From fa30a2cec5dfbf2b1f494b3a2c9d88651c0189b2 Mon Sep 17 00:00:00 2001 From: john-bv Date: Sun, 20 Aug 2023 16:59:11 -0500 Subject: [PATCH 73/75] update resource --- resources/header.css | 20 ++++++++++++++++++++ resources/header.html | 1 + 2 files changed, 21 insertions(+) create mode 100644 resources/header.css create mode 100644 resources/header.html diff --git a/resources/header.css b/resources/header.css new file mode 100644 index 0000000..b2172cf --- /dev/null +++ b/resources/header.css @@ -0,0 +1,20 @@ + +.warning-2 { + background: rgba(255,240,76,0.34) !important; + padding: 0.75em; + border-left: 2px solid #fce811; +} + +.warning-2 code { + background: rgba(211,201,88,0.64) !important; +} + +.notice-2 { + background: rgba(76, 240, 255, 0.629) !important; + padding: 0.75em; + border-left: 2px solid #4c96ff; +} + +.notice-2 code { + background: rgba(88, 211, 255, 0.64) !important; +} \ No newline at end of file diff --git a/resources/header.html b/resources/header.html new file mode 100644 index 0000000..490d336 --- /dev/null +++ b/resources/header.html @@ -0,0 +1 @@ + \ No newline at end of file From 1d2ac3b0929679f4c3406621bd1f95c8b7bfc8af Mon Sep 17 00:00:00 2001 From: john-bv Date: Sun, 20 Aug 2023 21:03:28 -0500 Subject: [PATCH 74/75] doc: Add documentation --- Cargo.toml | 5 +- README.md | 4 +- src/client/mod.rs | 127 +++++++++++++++++++++- src/connection/mod.rs | 159 ++++++++++++++++++++++++--- src/connection/state.rs | 2 +- src/error/client.rs | 10 ++ src/error/connection.rs | 4 + src/error/server.rs | 7 ++ src/lib.rs | 24 ++--- src/protocol/ack.rs | 27 ++--- src/protocol/mod.rs | 51 ++++++++- src/protocol/packet/mod.rs | 19 ++-- src/protocol/packet/offline.rs | 134 +++++++++++++++++++++-- src/protocol/packet/online.rs | 38 ++++++- src/protocol/reliability.rs | 124 ++++++++++++++++++++- src/server/mod.rs | 190 +++++++++++++++++++++++++++++++-- src/util/mod.rs | 48 ++++++++- 17 files changed, 883 insertions(+), 90 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 40d7562..515152b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "rak-rs" -version = "0.3.0-rc.5" +version = "0.1.0" authors = ["Bavfalcon9 "] edition = "2021" +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", "./resources/header.html"] + [features] default = [ "async_std" ] # default = ["async_tokio" ] diff --git a/README.md b/README.md index f410b1f..140ed86 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ RakNet (rak-rs) is available on [crates.io](), to use it, add the following to y ```toml [dependencies] -rakrs = "0.3.0" +rakrs = "0.1.0" ``` ## Features @@ -31,7 +31,7 @@ rak-rs also provides the following modules: - [`rak_rs::error`](https://docs.rs/rak-rs/latest/rak-rs/error) - A module with errors that both the Client and Server can respond with. - [`rak_rs::protocol`](https://docs.rs/rak-rs/latest/rak-rs/protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. - [`rak_rs::server`](https://docs.rs/rak-rs/latest/rak-rs/server) - The base server implementation of RakNet. -- [`rak_rs::utils`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. +- [`rak_rs::util`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. # Client diff --git a/src/client/mod.rs b/src/client/mod.rs index b248fa4..29cb62c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,3 +1,39 @@ +//! This module contains the client implementation of RakNet. +//! This module allows you to connect to a RakNet server, and +//! send and receive packets. This is the bare-bones implementation +//! for a RakNet client. +//! +//! # Getting Started +//! Connecting to a server is extremely easy with `rak-rs`, and can be done in a few lines of code. +//! In the following example we connect to `my_server.net:19132` and send a small packet, then wait +//! for a response, then close the connection when we're done. +//! +//! ```rust ignore +//! use rak_rs::client::{Client, DEFAULT_MTU}; +//! +//! #[async_std::main] +//! async fn main() { +//! let version: u8 = 10; +//! let mut client = Client::new(version, DEFAULT_MTU); +//! +//! if let Err(_) = client.connect("my_server.net:19132").await { +//! println!("Failed to connect to server!"); +//! return; +//! } +//! +//! println!("Connected to server!"); +//! +//! client.send_ord(vec![254, 0, 1, 1], Some(1)); +//! +//! loop { +//! let packet = client.recv().await.unwrap(); +//! println!("Received a packet! {:?}", packet); +//! break; +//! } +//! +//! client.close().await; +//! } +//! ``` pub mod discovery; pub mod handshake; @@ -68,8 +104,63 @@ pub const DEFAULT_MTU: u16 = 1400; use self::handshake::{ClientHandshake, HandshakeStatus}; -/// This struct is used to connect to RakNet servers. -/// To start a connection, use `Client::connect()`. +/// This is the client implementation of RakNet. +/// This struct includes a few designated methods for sending and receiving packets. +/// - [`Client::send_ord()`] - Sends a packet with the [`Reliability::ReliableOrd`] reliability. +/// - [`Client::send_seq()`] - Sends a packet with the [`Reliability::ReliableSeq`] reliability. +/// - [`Client::send()`] - Sends a packet with a custom reliability. +/// +/// # Ping Example +/// This is a simple example of how to use the client, this example will ping a server and print the latency. +/// ```rust ignore +/// use rak_rs::client::{Client, DEFAULT_MTU}; +/// use std::net::UdpSocket; +/// use std::sync::Arc; +/// +/// #[async_std::main] +/// async fn main() { +/// let mut socket = UdpSocket::bind("my_cool_server.net:19193").unwrap(); +/// let socket_arc = Arc::new(socket); +/// if let Ok(pong) = Client::ping(socket).await { +/// println!("Latency: {}ms", pong.pong_time - pong.ping_time); +/// } +/// } +/// ``` +/// +/// # Implementation Example +/// In the following example we connect to `my_server.net:19132` and send a small packet, then wait +/// for a response, then close the connection when we're done. +/// +/// ```rust ignore +/// use rak_rs::client::{Client, DEFAULT_MTU}; +/// +/// #[async_std::main] +/// async fn main() { +/// let version: u8 = 10; +/// let mut client = Client::new(version, DEFAULT_MTU); +/// +/// if let Err(_) = client.connect("my_server.net:19132").await { +/// println!("Failed to connect to server!"); +/// return; +/// } +/// +/// println!("Connected to server!"); +/// +/// client.send_ord(vec![254, 0, 1, 1], Some(1)); +/// +/// loop { +/// let packet = client.recv().await.unwrap(); +/// println!("Received a packet! {:?}", packet); +/// break; +/// } +/// +/// client.close().await; +/// } +/// ``` +/// +/// [`Client::send_ord()`]: crate::client::Client::send_ord +/// [`Client::send_seq()`]: crate::client::Client::send_seq +/// [`Client::send()`]: crate::client::Client::send pub struct Client { /// The connection state of the client. pub(crate) state: Arc>, @@ -99,7 +190,16 @@ pub struct Client { impl Client { /// Creates a new client. - /// > Note: This does not start a connection. You must use `Client::connect()` to start a connection. + /// > Note: This does not start a connection. You must use [Client::connect()] to start a connection. + /// + /// # Example + /// ```rust ignore + /// use rak_rs::client::Client; + /// + /// let mut client = Client::new(10, 1400); + /// ``` + /// + /// [Client::connect()]: crate::client::Client::connect pub fn new(version: u8, mtu: u16) -> Self { let (internal_send, internal_recv) = bounded::>(10); Self { @@ -118,8 +218,24 @@ impl Client { } } - /// Connects to a RakNet server. - /// > Note: This is an async function. You must await it. + /// This method should be used after [`Client::new()`] to start the connection. + /// This method will start the connection, and will return a [`ClientError`] if the connection fails. + /// + /// # Example + /// ```rust ignore + /// use rak_rs::client::Client; + /// + /// #[async_std::main] + /// async fn main() { + /// let mut client = Client::new(10, 1400); + /// if let Err(_) = client.connect("my_server.net:19132").await { + /// println!("Failed to connect to server!"); + /// return; + /// } + /// } + /// ``` + /// + /// [`Client::new()`]: crate::client::Client::new pub async fn connect Into>>( &mut self, addr: Addr, @@ -808,6 +924,7 @@ impl Client { impl Drop for Client { fn drop(&mut self) { + // todo: There is DEFINITELY a better way to do this... futures_executor::block_on(async move { self.close_notifier.lock().await.notify().await }); } } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index d9ab012..6e023a7 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -1,3 +1,47 @@ +//! This module contains the logic to handle a connection or "peer" to the server. +//! This is used internally by the server, and is not intended to be used by the end user outside +//! of the server. +//! +//! This module contains the following: +//! - [`Connection`]: The connection struct, which is used to hold the connection state. +//! - [`ConnectionState`]: The connection state enum, which is used to hold the state of the connection. +//! - [`ConnectionMeta`]: The connection meta struct, which is used to hold the meta information of the connection. +//! +//! This module also contains the following submodules: +//! - [`controller`]: The controller submodule, which is used to handle relability of the connection. +//! - [`queue`]: The queue submodule, which is used to handle the connection queues. +//! - [`state`]: The state submodule, which is used to handle the connection state. +//! +//! # Example +//! This is a snippet of code you would use after you've accepted a connection from the server with +//! [`Listener::accept()`]. +//! +//! ```ignore +//! use rakrs::connection::Connection; +//! +//! async fn handle(mut conn: Connection) { +//! loop { +//! // get a packet sent from the client +//! if let Ok(pk) = conn.recv().await { +//! println!("Got a connection packet {:?} ", pk); +//! } +//! +//! if !conn.state.lock().await.unwrap().is_available() { +//! conn.disconnect("Client disconnected.", false); +//! println!("Client disconnected!"); +//! break; +//! } +//! } +//! } +//! ``` +//! +//! [`Listener::accept()`]: crate::server::Listener::accept +//! [`Connection`]: crate::connection::Connection +//! [`ConnectionState`]: crate::connection::state::ConnectionState +//! [`ConnectionMeta`]: crate::connection::ConnectionMeta +//! [`controller`]: crate::connection::controller +//! [`queue`]: crate::connection::queue +//! [`state`]: crate::connection::state pub mod controller; /// Necessary queues for the connection. pub mod queue; @@ -44,7 +88,7 @@ use crate::{ frame::FramePacket, packet::{ offline::OfflinePacket, - online::{ConnectedPing, ConnectedPong, ConnectionAccept, OnlinePacket}, + online::{ConnectedPing, ConnectedPong, ConnectionAccept, Disconnect, OnlinePacket}, }, reliability::Reliability, }, @@ -77,19 +121,52 @@ impl ConnMeta { } } -/// This struct is utilized internally and represented -/// as per each "connection" or "socket" to the server. -/// This is **NOT** a struct that supports connecting TO -/// a RakNet instance, but rather a struct that HOLDS a -/// inbound connection connecting to a `Listener` instance. +/// The connection struct contains the logic for a connection to the server. +/// The following methods are the most important: +/// - [`Connection::recv()`]: This is used to recieve packets from the client. +/// - [`Connection::send()`]: This is used to send packets to the client. +/// - [`Connection::close()`]: This is used to disconnect the client. /// -/// Each Connection has it's own channel for recieving -/// buffers that come from this address. +/// +/// +///

+/// Warning: +///

+/// This struct does not provide an API for connecting to other peers, for that +/// you should use the +/// +/// Client +/// +/// struct. +///

+///
pub struct Connection { /// The address of the connection /// This is internally tokenized by rak-rs pub address: SocketAddr, - pub(crate) state: Arc>, + pub state: Arc>, /// The queue used to send packets back to the connection. send_queue: Arc>, /// The queue used to recieve packets, this is read from by the server. @@ -145,13 +222,13 @@ impl Connection { let tk = c.tasks.clone(); let mut tasks = tk.lock().await; tasks.push(c.init_tick(notifier)); - tasks.push(c.init_net_recv(net, net_sender).await); + tasks.push(c.init_net_recv(net, net_sender)); return c; } /// Initializes the client ticking process! - pub fn init_tick(&self, notifier: Arc>) -> task::JoinHandle<()> { + pub(crate) fn init_tick(&self, notifier: Arc>) -> task::JoinHandle<()> { let address = self.address; let closer = self.disconnect.clone(); let last_recv = self.recv_time.clone(); @@ -300,7 +377,7 @@ impl Connection { /// This function initializes the raw internal packet handling task! /// - pub async fn init_net_recv( + pub(crate) fn init_net_recv( &self, // THIS IS ONLY ACTIVATED ON STD #[cfg(feature = "async_std")] net: Receiver>, @@ -469,7 +546,7 @@ impl Connection { }); } - pub async fn process_packet( + pub(crate) async fn process_packet( buffer: &[u8], address: &SocketAddr, sender: &Sender>, @@ -590,7 +667,28 @@ impl Connection { Ok(false) } - /// Recieve a packet from the client. + /// This method is used to recieve packets from the client connection. + /// Packets that are recieved here are packets sent by the peer expected + /// to be handled by the server. + /// + /// This is where you would handle your packets from the client. + /// + /// # Example + /// This is a snippet of code you would use after you've accepted a connection from the server with + /// [`Listener::accept()`]. + /// + /// ```ignore + /// use rakrs::connection::Connection; + /// + /// async fn handle(mut conn: Connection) { + /// loop { + /// // get a packet sent from the client + /// if let Ok(pk) = conn.recv().await { + /// println!("Got a connection packet {:?} ", pk); + /// } + /// } + /// } + /// ``` pub async fn recv(&mut self) -> Result, RecvError> { #[allow(unused_mut)] let mut q = self.internal_net_recv.as_ref().lock().await; @@ -641,8 +739,18 @@ impl Connection { !self.state.lock().await.is_available() } - /// Send a packet to the client. - /// These will be sent next tick unless otherwise specified. + /// This method is used to send payloads to the connection. This method internally + /// will encode your payload into a RakNet packet, and send it to the client. + /// + /// # Example + /// This is a snippet of code you would use when you need to send a payload to the client. + /// ```ignore + /// use rakrs::connection::Connection; + /// + /// async fn send_payload(mut conn: Connection) { + /// conn.send(&[0x01, 0x02, 0x03], true).await.unwrap(); + /// } + /// ``` pub async fn send(&self, buffer: &[u8], immediate: bool) -> Result<(), SendQueueError> { let mut q = self.send_queue.write().await; if let Err(e) = q @@ -654,12 +762,31 @@ impl Connection { Ok(()) } + /// This method should be used when you are ready to disconnect the client. + /// this method will attempt to send a disconnect packet to the client, and + /// then close the connection. pub async fn close(&mut self) { rakrs_debug!( true, "[{}] Dropping connection!", to_address_token(self.address) ); + if let Err(_) = self + .send( + &OnlinePacket::Disconnect(Disconnect {}) + .write_to_bytes() + .unwrap() + .as_slice(), + true, + ) + .await + { + rakrs_debug!( + true, + "[{}] Failed to send disconnect packet when closing!", + to_address_token(self.address) + ); + } let tasks = self.tasks.clone(); for task in tasks.lock().await.drain(..) { diff --git a/src/connection/state.rs b/src/connection/state.rs index 6891ec4..d783f96 100644 --- a/src/connection/state.rs +++ b/src/connection/state.rs @@ -1,6 +1,6 @@ /// Connection States /// These are all possible states of a raknet session, and while accessible externally -/// Please note that these are not states relied in the original implementation of +/// Please note that these are not states relied on within the original implementation of /// raknet, which preserve both "Unconnected" and "Connected" #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub enum ConnectionState { diff --git a/src/error/client.rs b/src/error/client.rs index 1b04145..a8c848f 100644 --- a/src/error/client.rs +++ b/src/error/client.rs @@ -1,14 +1,24 @@ +//! Client errors are errors that can occur when using the [`Client`](crate::client::Client) api. use crate::connection::queue::SendQueueError; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ClientError { + /// The client is already connected to the peer on this address. AddrBindErr, + /// The client is already connected a peer. AlreadyOnline, + /// The client is offline and can not send packets. NotListening, + /// The client is unable to connect to the peer. Unavailable, + /// The client is unable to connect to the peer because the peer is using a different protocol version. IncompatibleProtocolVersion, + /// The client has been closed. Killed, + /// The client has been closed, and can not be used again. Reset, + /// The client is unable to connect to the peer because the peer is offline. ServerOffline, + /// The client failed to process a packet you sent. SendQueueError(SendQueueError), } diff --git a/src/error/connection.rs b/src/error/connection.rs index d2a54ab..f16a1ea 100644 --- a/src/error/connection.rs +++ b/src/error/connection.rs @@ -1,3 +1,7 @@ +//! # Connection Error +//! These error types are used when an error occurs within the [`Connection`]. +//! +//! [`Connection`]: crate::connection::Connection #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ConnectionError { Closed, diff --git a/src/error/server.rs b/src/error/server.rs index b54929d..3bae157 100644 --- a/src/error/server.rs +++ b/src/error/server.rs @@ -1,8 +1,15 @@ +//! Server errors +//! Server errors are errors that can occur when using the [`Listener`](crate::server::Listener) api. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ServerError { + /// The server is unable to bind to the given address. AddrBindErr, + /// The server is already online and can not be started again. AlreadyOnline, + /// The server is offline and can not send packets. NotListening, + /// The server has been closed. Killed, + /// The server has been closed, and can not be used again. Reset, } diff --git a/src/lib.rs b/src/lib.rs index c7c03ec..2d2c09c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! //! ```toml //! [dependencies] -//! rakrs = "0.3.0" +//! rakrs = "0.1.0" //! ``` //! //! ## Features @@ -19,19 +19,19 @@ //! //! ```toml //! [dependencies] -//! rak_rs = { version = "0.3.0", default-features = false, features = [ "async_tokio", "mcpe" ] } +//! rak_rs = { version = "0.1.0", default-features = false, features = [ "async_tokio", "mcpe" ] } //! ``` //! //! //! //! rak-rs also provides the following modules: //! -//! - [`rak_rs::client`](https://docs.rs/rak-rs/latest/rak-rs/client) - A client implementation of RakNet, allowing you to connect to a RakNet server. -//! - [`rak_rs::connection`](https://docs.rs/rak-rs/latest/rak-rs/client) - A bare-bones implementation of a Raknet peer, this is mainly used for types. -//! - [`rak_rs::error`](https://docs.rs/rak-rs/latest/rak-rs/error) - A module with errors that both the Client and Server can respond with. -//! - [`rak_rs::protocol`](https://docs.rs/rak-rs/latest/rak-rs/protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. -//! - [`rak_rs::server`](https://docs.rs/rak-rs/latest/rak-rs/server) - The base server implementation of RakNet. -//! - [`rak_rs::utils`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. +//! - [`rak_rs::client`](crate::client) - A client implementation of RakNet, allowing you to connect to a RakNet server. +//! - [`rak_rs::connection`](crate::connection) - A bare-bones implementation of a Raknet peer, this is mainly used for types. +//! - [`rak_rs::error`](crate::error) - A module with errors that both the Client and Server can respond with. +//! - [`rak_rs::protocol`](crate::protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. +//! - [`rak_rs::server`](crate::server) - The base server implementation of RakNet. +//! - [`rak_rs::util`](crate::util) - General utilities used within `rak-rs`. //! //! # Client //! @@ -48,15 +48,15 @@ //! let version: u8 = 10; //! let addr = "my_server.net:19132".to_socket_addrs().unwrap(); //! let mut client = Client::new(version, DEFAULT_MTU); -//! +//! //! client.connect(addr.next().unwrap()).await.unwrap(); -//! +//! //! // receive packets //! loop { //! let packet = client.recv().await.unwrap(); -//! +//! //! println!("Received a packet! {:?}", packet); -//! +//! //! client.send_ord(vec![254, 0, 1, 1], Some(1)); //! } //! } diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs index 5352111..954a9ba 100644 --- a/src/protocol/ack.rs +++ b/src/protocol/ack.rs @@ -3,7 +3,10 @@ pub const NACK: u8 = 0xa0; use std::ops::Range; -use binary_util::{BinaryIo, types::{u24, LE}}; +use binary_util::{ + types::{u24, LE}, + BinaryIo, +}; pub(crate) trait Ackable { type NackItem; @@ -85,22 +88,14 @@ impl Ack { } else { // end of range if current.start == current.end { - records.push( - Record::Single( - SingleRecord { - sequence: LE(current.start.into()), - } - ) - ); + records.push(Record::Single(SingleRecord { + sequence: LE(current.start.into()), + })); } else { - records.push( - Record::Range( - RangeRecord { - start: LE(current.start.into()), - end: LE(current.end.into()), - } - ) - ); + records.push(Record::Range(RangeRecord { + start: LE(current.start.into()), + end: LE(current.end.into()), + })); } current.start = m; diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 1c324d4..75992f8 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,19 +1,66 @@ +//! Protocol implementation for RakNet. +//! this module contains all of the necessary packets to communicate with RakNet servers. +//! +//! ## Example +//! Please note this example is not a copy-paste example, but rather a snippet of code +//! that shows how to use the [`protocol`] module. +//! +//! ```rust ignore +//! use rakrs::protocol::packet::RakPacket; +//! use binary_util::BinaryIo; +//! +//! fn decode_packet(packet: &[u8]) { +//! let packet = RakPacket::read_from_slice(packet).unwrap(); +//! +//! match packet { +//! RakPacket::Offline(packet) => match packet { +//! OfflinePacket::UnconnectedPing(packet) => { +//! println!("Received a ping packet! {:?}", packet); +//! }, +//! _ => {} +//! }, +//! _ => {} +//! } +//! }; +//! ``` +/// This is an internal module that contains the logic to implement the Ack system within +/// RakNet. pub(crate) mod ack; +/// This is an internal module that contains the logic to implement the frame system within +/// RakNet. This is also called the "Datagram" or "Encapsulated" packet in different implementations. +/// +/// You can find the original implementation from RakNet [here](https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/ReliabilityLayer.cpp#L110-L231) pub(crate) mod frame; +/// This is the constant added to all offline packets to identify them as RakNet packets. pub(crate) mod magic; +/// This module contains the MCPE specific packets that are used within RakNet, this is guarded +/// under the `mcpe` feature. +/// +/// To enable this feature, add the following to your `Cargo.toml`: +/// ```toml +/// [dependencies] +/// rak-rs = { version = "0.1.0", features = [ "mcpe" ] } +/// ``` pub mod mcpe; pub mod packet; pub mod reliability; pub use magic::*; +/// The maximum amount of fragments that can be sent within a single frame. +/// This constant is used to prevent a client from sending too many fragments, +/// or from bad actors from sending too many fragments. pub const MAX_FRAGS: u32 = 1024; +/// The maximum amount of channels that can be used on a single connection. +/// This is a raknet limitation, and is not configurable. pub const MAX_ORD_CHANS: u8 = 32; -// IP Header + UDP Header + RakNet Header + RakNet Frame Packet Header (MAX) +/// IP Header + UDP Header + RakNet Header + RakNet Frame Packet Header (MAX) pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20; -// IP Header + UDP Header + RakNet Header +/// IP Header + UDP Header + RakNet Header pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; +/// The maximum possible amount of bytes that can be sent within a single frame. pub const MTU_MAX: u16 = 2400; +/// The minimum possible amount of bytes that can be sent within a single frame. pub const MTU_MIN: u16 = 400; diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs index 87ee62c..9c2f0c4 100644 --- a/src/protocol/packet/mod.rs +++ b/src/protocol/packet/mod.rs @@ -1,20 +1,27 @@ +//! This module contains all the packets that are used by the RakNet protocol. +//! This module is split into two submodules: +//! - [`offline`]: Any packet that is not sent within a [`Frame`]. +//! - [`online`]: Any packet considered to be online, which is sent within a [`Frame`]. +//! +//! [`offline`]: crate::protocol::packet::offline +//! [`online`]: crate::protocol::packet::online // /// Handlers for both online & offline packets! // /// This is used by the connection struct to handle packets. // pub(crate) mod handler; -/// The protocol that is used when a client is considered "Online". -/// Sessions in this state are usually: Connected or Connecting -pub mod online; - -/// The protocol that is used when a client is considered "Unidentified". -/// This module is used for Pong and connection requests. pub mod offline; +pub mod online; use binary_util::interfaces::{Reader, Writer}; use self::offline::OfflinePacket; use self::online::OnlinePacket; +/// A wrapper or helper for both online and offline packets. +/// This allows for a single type to be read with `Reader` and written with `Writer`, +/// traits provided by `binary_util`. +/// +/// All packets sent are wrapped in this type, and can be unwrapped with the `get_offline` or `get_online` #[derive(Debug, Clone)] pub enum RakPacket { Offline(OfflinePacket), diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 967fbe8..6edf215 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -1,3 +1,15 @@ +//! Offline packets are packets that are sent before a connection is established. +//! In rak-rs, these packets consist of: +//! - [`UnconnectedPing`] +//! - [`UnconnectedPong`] +//! - [`OpenConnectRequest`] +//! - [`OpenConnectReply`] +//! - [`SessionInfoRequest`] +//! - [`SessionInfoReply`] +//! - [`IncompatibleProtocolVersion`] +//! +//! During this stage, the client and server are exchanging information about each other, such as +//! the server id, the client id, the mtu size, etc, to prepare for the connection handshake. use std::net::SocketAddr; use super::RakPacket; @@ -11,7 +23,10 @@ use binary_util::interfaces::{Reader, Writer}; use binary_util::io::{ByteReader, ByteWriter}; use binary_util::BinaryIo; -/// A enum that represents all offline packets. +/// This is an enum of all offline packets. +/// +/// You can use this to read and write offline packets, +/// with the `binary_util` traits `Reader` and `Writer`. #[derive(Clone, Debug, BinaryIo)] #[repr(u8)] pub enum OfflinePacket { @@ -35,7 +50,12 @@ register_packets! { IncompatibleProtocolVersion } -/// Unconnected Ping +/// Send to the other peer expecting a [`UnconnectedPong`] packet, +/// this is used to determine the latency between the client and the server, +/// and to determine if the server is online. +/// +/// If the peer does not respond with a [`UnconnectedPong`] packet, the iniatior should +/// expect that the server is offline. #[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPing { pub timestamp: u64, @@ -43,7 +63,48 @@ pub struct UnconnectedPing { pub client_id: i64, } -/// Unconnected Pong +/// Sent in response to a [`UnconnectedPing`] packet. +/// This is used to determine the latency between the client and the server, and to determine +/// that the peer is online. +/// +/// +///
+/// Note: +///

+/// If the client is a Minecraft: Bedrock Edition client, this packet is not sent +/// and the +/// +/// UnconnectedPong +/// +/// from the mcpe module is sent instead. +///

+///
+/// +/// [`UnconnectedPong`]: crate::protocol::packet::offline::UnconnectedPong #[cfg(not(feature = "mcpe"))] #[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPong { @@ -53,6 +114,45 @@ pub struct UnconnectedPong { } /// This packet is the equivelant of the `OpenConnectRequest` packet in RakNet. +/// +/// This packet is sent by the peer to a server to request a connection. +/// It contains information about the client, such as the protocol version, and the mtu size. +/// The peer should expect a [`OpenConnectReply`] packet in response to this packet, if the +/// server accepts the connection. Otherwise, the peer should expect a [`IncompatibleProtocolVersion`] +/// packet to be sent to indicate that the server does not support the protocol version. +/// +/// +///
+/// Note: +///

+/// Internally this packet is padded by the given +/// mtu_size in the packet. This is done by appending null bytes +/// to the current buffer of the packet which is calculated by adding the difference +/// between the mtu_size and the current length. +///

+///
#[derive(Debug, Clone)] pub struct OpenConnectRequest { pub protocol: u8, // 9 @@ -85,8 +185,13 @@ impl Writer for OpenConnectRequest { } // Open Connection Reply -/// Sent to the client when the server accepts a client. -/// This packet is the equivalent of the `Open Connect Reply 1` packet. +/// This packet is sent in response to a [`OpenConnectRequest`] packet, and confirms +/// the information sent by the peer in the [`OpenConnectRequest`] packet. +/// +/// This packet is the equivalent of the `Open Connect Reply 1` within the original RakNet implementation. +/// +/// If the server chooses to deny the connection, it should send a [`IncompatibleProtocolVersion`] +/// or ignore the packet. #[derive(Debug, Clone, BinaryIo)] pub struct OpenConnectReply { pub magic: Magic, @@ -95,16 +200,29 @@ pub struct OpenConnectReply { pub mtu_size: u16, } -/// Session info, also known as Open Connect Request 2 +/// This packet is sent after receiving a [`OpenConnectReply`] packet, and confirms +/// that the peer wishes to proceed with the connection. The information within this packet +/// is primarily used to get the external address of the peer. +/// +/// This packet is the equivalent of the `Open Connect Request 2` within the original RakNet implementation. #[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoRequest { pub magic: Magic, + /// The socket address of the peer you are sending + /// this packet to. pub address: SocketAddr, + /// The mtu size of the peer you are sending this packet to. pub mtu_size: u16, + /// Your internal client id. pub client_id: i64, } -/// Session Info Reply, also known as Open Connect Reply 2 +/// This packet is sent in response to a [`SessionInfoRequest`] packet, and confirms +/// all the information sent by the peer in the [`SessionInfoRequest`] packet. This packet +/// also specifies the external address of the peer, as well as whether or not +/// encryption at the RakNet level is enabled on the server. +/// +/// This packet is the equivalent of the `Open Connect Reply 2` within the original RakNet implementation. #[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoReply { pub magic: Magic, @@ -114,6 +232,8 @@ pub struct SessionInfoReply { pub security: bool, } +/// This packet is sent by the server to indicate that the server does not support the +/// protocol version of the client. #[derive(Debug, Clone, BinaryIo)] pub struct IncompatibleProtocolVersion { pub protocol: u8, diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index 4c8f1c2..f41c291 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -1,3 +1,17 @@ +//! Online packets are packets that are sent when the client is connected to the server +//! and are used to keep the connection alive, and to send game packets to the server. +//! +//! The following module provides the following packets: +//! - [`ConnectedPing`] +//! - [`ConnectedPong`] +//! - [`LostConnection`] +//! - [`ConnectionRequest`] +//! - [`ConnectionAccept`] +//! - [`NewConnection`] +//! - [`Disconnect`] +//! +//! During this stage, the client and server are exchanging information about each other, +//! to initialize the connection within raknet, and completing the connection handshake. use std::net::SocketAddr; use super::RakPacket; @@ -7,6 +21,10 @@ use binary_util::interfaces::{Reader, Writer}; use binary_util::io::{ByteReader, ByteWriter}; use binary_util::BinaryIo; +/// An enum of all Online packets. +/// +/// You can use this to read and write online packets, +/// with the `binary_util` traits `Reader` and `Writer`. #[derive(BinaryIo, Clone, Debug)] #[repr(u8)] pub enum OnlinePacket { @@ -30,19 +48,29 @@ register_packets! { Disconnect } -/// Connected Ping Packet -/// This packet is sent by the client to the server. -/// The server should respond with a `ConnectedPong` packet. +/// This packet is sent by either the client or the server to the other peer. +/// The other peer should respond with a [`ConnectedPong`] packet. In general +/// you should be sending this packet every 5 seconds to keep the connection alive. +///
+///
+/// If you do not continue to send this packet, the connection will be closed after +/// the other peer does not receive a [`ConnectedPong`] packet for the configured timeout option. #[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPing { + /// The time you sent the packet to the peer. pub time: i64, } -/// Connected Pong Packet -/// This packet is sent by the server to the client in response to a `ConnectedPing` packet. +/// Sent in response to a [`ConnectedPing`] packet. +/// +/// This packet is sent by the other peer in response to a [`ConnectedPing`] packet as +/// an acknowledgement that the connection is still alive. It contains the time of the +/// round trip from the time that the initiator sent the [`ConnectedPing`] packet. #[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPong { + /// The time that the peer sent the [`ConnectedPing`] packet. pub ping_time: i64, + /// The time that you sent the [`ConnectedPong`] packet to the peer. pub pong_time: i64, } diff --git a/src/protocol/reliability.rs b/src/protocol/reliability.rs index 0c611bd..6b923ad 100644 --- a/src/protocol/reliability.rs +++ b/src/protocol/reliability.rs @@ -1,3 +1,58 @@ +//! Reliability types for packets. +//! Each packet sent on the RakNet protocol has a reliability type, which determines how the packet should be handled. +//! +//! ## Unreliable +//! Unreliable packets are sent without any guarantee that they will be received by the other peer. +//! In other words, the packet is sent and forgotten about, with no expectation of a response. +//! This is the fastest reliability type, as it does not require any acks to be sent +//! back to the sender. Generally used for packets that are not important, such as +//! [`UnconnectedPing`] and [`UnconnectedPong`]. +//! +//! ## Sequenced +//! Sequenced packets are sent with a sequence index in each frame (datagram) that is sent. +//! This is a number that is incremented after each packet. This allows us to implement a sliding window +//! ([`ReliableWindow`]), which +//! will discard any packets that are way too old or way too new. +//! +//! ## Ordered +//! Ordered packets are sent with an order index in each frame, as well as an order channel. +//! RakNet will use this information to order the packets in a specific way. +//! +//! The `order_channel` is used to determine which channel the packet should be sent on, in other words, +//! this is a unique channel chosen by the sender to send the packet on, and the receiver will use this +//! channel to re-order the packets in the correct order when they are sent. +//! +//! The `order_index` is used to determine the position of the packet in the order channel, this is +//! incremented after each packet is sent, similar to the sequence index, without the sliding window. +//! +//! ## Reliable +//! Reliable packets are sent with an ack system, meaning that the receiver will send an ack back to the +//! sender, which the sender expects to recieve. +//! +//! If the sender does not receive the ack, it will resend the packet until it receives the ack, or until +//! the connection is closed. +//! +//! [`UnconnectedPing`]: crate::protocol::packet::offline::UnconnectedPing +//! [`UnconnectedPong`]: crate::protocol::packet::offline::UnconnectedPong +//! [`ReliableWindow`]: crate::connection::controller::window::ReliableWindow +//! +/// The [RakNet Reliabilty] of a packet. +/// +/// This is a bit flag encoded within each [`Frame`] that determines how the packet should be handled. +/// As of writing the following reliability types are supported: +/// - [`Unreliable`] +/// - [`UnreliableSeq`] +/// - [`Reliable`] +/// - [`ReliableOrd`] +/// - [`ReliableSeq`] +/// +/// [RakNet Reliabilty]: https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/PacketPriority.h#L46-L85 +/// [`Frame`]: crate::protocol::frame::Frame +/// [`Unreliable`]: crate::protocol::reliability::Reliability::Unreliable +/// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq +/// [`Reliable`]: crate::protocol::reliability::Reliability::Reliable +/// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd +/// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq #[derive(Clone, Debug, Copy)] #[repr(u8)] pub enum Reliability { @@ -19,6 +74,11 @@ pub enum Reliability { } impl Reliability { + /// Creates a new [`Reliability`] from the given flags. + /// This is used internally to decode the reliability from the given + /// bit flags. + /// + /// [`Reliability`]: crate::protocol::reliability::Reliability pub fn from_flags(flags: u8) -> Self { match (flags & 224) >> 5 { 0 => Reliability::Unreliable, @@ -34,6 +94,8 @@ impl Reliability { } } + /// Converts the [`Reliability`] into a bit flag. + /// This is useful for encoding the reliability into a packet. pub fn to_flags(&self) -> u8 { match self { Reliability::Unreliable => 0 << 5, @@ -47,7 +109,15 @@ impl Reliability { } } - /// Whether or not the packet is ordered. + /// This method checks whether the reliability is ordered, meaning that the packets + /// are either: + /// - [`ReliableOrd`] + /// - [`ReliableOrdAck`] + /// - [`UnreliableSeq`] + /// + /// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd + /// [`ReliableOrdAck`]: crate::protocol::reliability::Reliability::ReliableOrdAck + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq pub fn is_ordered(&self) -> bool { match self { Self::UnreliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => true, @@ -55,7 +125,19 @@ impl Reliability { } } - /// Whether or not the packet is reliable. + /// Verifies whether or not the reliabilty is reliable, meaning that the packets + /// are either: + /// - [`Reliable`] + /// - [`ReliableOrd`] + /// - [`ReliableSeq`] + /// - [`ReliableAck`] + /// + /// Other reliabilities are not reliable, and will return `false`. + /// + /// [`Reliable`]: crate::protocol::reliability::Reliability::Reliable + /// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd + /// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq + /// [`ReliableAck`]: crate::protocol::reliability::Reliability::ReliableAck pub fn is_reliable(&self) -> bool { match self { Self::Reliable | Self::ReliableOrd | Self::ReliableSeq | Self::ReliableOrdAck => true, @@ -63,7 +145,17 @@ impl Reliability { } } - /// Whether or not the packet is unreliable. + /// Verifies whether or not the reliabilty is unreliable, meaning that the packets + /// are either: + /// - [`Unreliable`] + /// - [`UnreliableSeq`] + /// - [`UnreliableAck`] + /// + /// Other reliabilities are not unreliable, and will return `false`. + /// + /// [`Unreliable`]: crate::protocol::reliability::Reliability::Unreliable + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq + /// [`UnreliableAck`]: crate::protocol::reliability::Reliability::UnreliableAck pub fn is_unreliable(&self) -> bool { match self { Self::Unreliable | Self::UnreliableSeq | Self::UnreliableAck => true, @@ -71,7 +163,20 @@ impl Reliability { } } - /// Whether or not the packet is sequenced. + /// Verifies whether or not the reliabilty is sequenced, meaning that the packets + /// are either: + /// - [`UnreliableSeq`] + /// - [`ReliableSeq`] + /// + /// Other reliabilities are not sequenced, and will return `false`. + /// + /// ## What is a sequenced packet? + /// A sequenced packet is a packet with an index that is incremented after each packet. + /// RakNet uses this internally to discard packets that are sent out of sequence, accepting + /// only the latest packet. + /// + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq + /// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq pub fn is_sequenced(&self) -> bool { match self { Self::UnreliableSeq | Self::ReliableSeq => true, @@ -79,6 +184,12 @@ impl Reliability { } } + /// Verifies whether or not the reliabilty is sequenced or ordered. This function + /// is a combination of [`Reliability::is_sequenced`] and [`Reliability::is_ordered`], + /// combined into one signature. + /// + /// [`Reliability::is_sequenced`]: crate::protocol::reliability::Reliability::is_sequenced + /// [`Reliability::is_ordered`]: crate::protocol::reliability::Reliability::is_ordered pub fn is_sequenced_or_ordered(&self) -> bool { match self { Self::UnreliableSeq | Self::ReliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => { @@ -88,7 +199,10 @@ impl Reliability { } } - /// Whether or not the packet has an acknowledgement. + /// Verifies that the reliability is an ack ([`Ack`]). + /// This is primarily used internally to determine whether or not to send an ack. + /// + /// [`Ack`]: crate::protocol::ack::Ack pub fn is_ack(&self) -> bool { match self { Self::UnreliableAck | Self::ReliableAck | Self::ReliableOrdAck => true, diff --git a/src/server/mod.rs b/src/server/mod.rs index c0c2e9c..d443cb3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,9 @@ +//! This is the server implementation of RakNet, allowing you to create a RakNet server. +//! +//! This module provides a [`Listener`] struct, which is responsible for listening to connections, +//! and dispatching them to a handler, as well as some other utilities. +//! +//! [`Listener`]: struct.Listener.html #[allow(unused)] /// Server events module. Handles things like updating the MOTD /// for certain connections. This is a notifier channel. @@ -16,17 +22,17 @@ use async_std::{ #[cfg(feature = "async_std")] use futures::{select, FutureExt}; -use binary_util::ByteReader; use binary_util::interfaces::{Reader, Writer}; +use binary_util::ByteReader; #[cfg(feature = "async_tokio")] use tokio::{ net::UdpSocket, + select, sync::mpsc::channel as bounded, sync::mpsc::{Receiver, Sender}, sync::Mutex, task::{self}, - select }; use crate::connection::{ConnMeta, Connection}; @@ -41,9 +47,15 @@ use crate::protocol::Magic; use crate::rakrs_debug; use crate::util::to_address_token; -pub type Session = (ConnMeta, Sender>); +pub(crate) type Session = (ConnMeta, Sender>); -// stupid hack for easier syntax :) +/// This is a helper enum that allows you to pass in a `SocketAddr` or a `&str` to the `Listener::bind` function. +/// This is useful for when you want to bind to a specific address, but you don't want to parse it yourself. +/// +/// This Trait will successfully parse the following: +/// - `SocketAddr::new("127.0.0.1:19132")` +/// - `"127.0.0.1:19132"` +/// - `String::from("127.0.0.1:19132")` pub enum PossiblySocketAddr<'a> { SocketAddr(SocketAddr), Str(&'a str), @@ -97,6 +109,98 @@ impl std::fmt::Display for PossiblySocketAddr<'_> { } } +/// The main server struct, this is responsible for listening to connections, and dispatching them to a handler. +/// > If you are having problems with debugging, you can use the rak-rs debug feature, which will print out +/// > all packets that are sent and recieved. +/// +/// +/// +///
+/// Notice: +///

+/// Currently, the Listener does not support encryption, plugins, or any feature to allow you to +/// hijack the RakNet connection sequence. Currently rak_rs is a pure, bare-bones RakNet implementation.

+/// There is currently an open issue +/// to add support for plugins but this is not a priority, instead you should use the Connection struct +/// to handle your own packets with the recv method. +///

+///
+/// +/// ## A generic example +/// ```rust ignore +/// use rak_rs::server::Listener; +/// +/// #[async_std::main] +/// async fn main() { +/// // Bind the server to the specified address, but do not start it. +/// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +/// +/// // Begins listening to connections +/// server.start().await.unwrap(); +/// +/// // Start recieving connections +/// loop { +/// let conn = server.accept().await; +/// async_std::task::spawn(handle(conn.unwrap())); +/// } +/// } +/// +/// // This is a function that handles the connection, this is where you would handle the connection. +/// async fn handle(mut conn: Connection) { +/// loop { +/// // this is used to cleanup the connection +/// if conn.get_state().await.is_closed() { +/// println!("Connection closed!"); +/// break; +/// } +/// +/// if let Ok(pk) = conn.recv().await { +/// println!("Got a connection packet {:?} ", pk); +/// } +/// } +/// } +/// ``` +/// +/// ## Accepting other protocols +/// This struct allows support for other raknet protocols, however this is not recommended, because occasionally +/// the protocol may change and the Listener may not be updated to support it. This was mainly added for MCPE. +/// +/// ```rust ignore +/// use rak_rs::server::Listener; +/// +/// #[async_std::main] +/// async fn main() { +/// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +/// server.versions = &[10, 11]; +/// server.start().await.unwrap(); +/// +/// loop { +/// // .. same loop as above +/// } +/// } +/// ``` pub struct Listener { /// If mcpe is true, this is the default MOTD, this is /// the default MOTD to send to the client. You can change this later by setting @@ -130,7 +234,20 @@ pub struct Listener { } impl Listener { - /// Binds a socket to the specified addres and starts listening. + /// Binds a new listener to the specified address provided, this will error if the address is invalid or already in use. + /// This will not start the listener, you must call [`Listener::start`] to start listening to connections. + /// + /// ## Example + /// ```ignore + /// use rak_rs::server::Listener; + /// + /// async fn start() { + /// let mut server = Listener::bind("").await.unwrap(); + /// } + /// ``` + /// + /// [`PossiblySocketAddr`]: enum.PossiblySocketAddr.html + /// [`Listener::start`]: struct.Listener.html#method.start pub async fn bind Into>>( address: I, ) -> Result { @@ -148,6 +265,8 @@ impl Listener { Err(_) => return Err(ServerError::AddrBindErr), }; + rakrs_debug!(true, "listener: Bound to {}", address); + let server_id: u64 = rand::random(); let motd = Motd::new(server_id, format!("{}", address.port())); @@ -179,8 +298,11 @@ impl Listener { return Ok(listener); } - /// Starts the listener! - /// TODO + /// This method is required to be called before the server can begin listening to connections. + /// However, you must call [`Listener::bind`] before you can call this method, as that method + /// is responsible for creating the socket and initializing the server. + /// + /// ## Example /// ```ignore /// use rak_rs::server::Listener; /// async fn start() { @@ -190,6 +312,8 @@ impl Listener { /// server.start().await; /// } /// ``` + /// + /// [`Listener::bind`]: struct.Listener.html#method.bind pub async fn start(&mut self) -> Result<(), ServerError> { if self.serving { return Err(ServerError::AlreadyOnline); @@ -464,7 +588,9 @@ impl Listener { addr = client_close_recv.recv().fuse() => { if let Ok(addr) = addr { rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); - connections2.lock().await.remove(&addr); + let mut c = connections2.lock().await; + c.remove(&addr); + drop(c); } } } @@ -478,7 +604,9 @@ impl Listener { addr = client_close_recv.recv() => { if let Some(addr) = addr { rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); - connections2.lock().await.remove(&addr); + let mut c = connections2.lock().await; + c.remove(&addr); + drop(c); } } } @@ -507,9 +635,45 @@ impl Listener { // } // } - /// Must be called in after both `Listener::bind` AND `Listener::start`. This function + /// This method is used to accept a connection, this will block until a connection is available. + /// You can only call this method once both [`Listener::bind`] AND [`Listener::start`] have. This function /// is used to recieve and accept connections. Alternatively, you can refuse a connection /// by dropping it when you accept it. + /// + /// [`Listener::bind`]: struct.Listener.html#method.bind + /// [`Listener::start`]: struct.Listener.html#method.start + /// + ///
+ /// Warning: + ///

+ /// This method will block until a connection is available, this is not recommended to be used + /// in the main thread, instead you should use a task or future to handle connections. + ///

+ ///
+ /// + /// ## Example + /// ```rust ignore + /// use rak_rs::server::Listener; + /// use rak_rs::Connection; + /// + /// #[async_std::main] + /// async fn main() { + /// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + /// server.start().await.unwrap(); + /// + /// loop { + /// let conn = server.accept().await; + /// async_std::task::spawn(handle(conn.unwrap())); + /// } + /// } + /// + /// async fn handle(mut conn: Connection) { + /// loop { + /// let packet = conn.recv().await; + /// println!("Received a packet! {:?}", packet); + /// } + /// } + /// ``` pub async fn accept(&mut self) -> Result { if !self.serving { Err(ServerError::NotListening) @@ -528,7 +692,11 @@ impl Listener { } } - /// Stops the listener and frees the socket. + /// Stops the Listener, effectively closing the socket and stopping the server. + /// This will also close all connections, and prevent any new connections from being accepted, + /// until [`Listener::start`] is called again. + /// + /// [`Listener::start`]: struct.Listener.html#method.start pub async fn stop(&mut self) -> Result<(), ServerError> { self.closed.notify().await; // self.cleanup.notified().await; diff --git a/src/util/mod.rs b/src/util/mod.rs index c54764e..3efa540 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] #[cfg(feature = "async_std")] use async_std::task::sleep as async_sleep; use std::net::{SocketAddr, ToSocketAddrs}; @@ -50,7 +51,48 @@ impl_gen!(usize); /// old and un-used values. Key serves as a `packet_id` in /// rakrs, but this could be used else-where. /// -/// ## Deprecated in favor of `RecoveryQueue` +/// +/// +///
+/// Warning: +///

+/// This struct will be removed in 0.2.0 in favor of RecoveryQueue. +///

+///
+/// /// /// Usage example: /// ```rust @@ -63,6 +105,10 @@ impl_gen!(usize); /// myStore.flush(); /// ``` #[derive(Debug, Clone)] +#[deprecated( + since = "0.0.1", + note = "This is deprecated in favor of `RecoveryQueue`" +)] pub struct CacheStore { pub(crate) store: HashMap)>, } From af5dfba6c3cb95aa08a822d39114015908dfc795 Mon Sep 17 00:00:00 2001 From: john-bv Date: Sun, 20 Aug 2023 21:11:22 -0500 Subject: [PATCH 75/75] doc(README.md): Update readme to contain right info --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 140ed86..b22ada4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ RakNet (rak-rs) is available on [crates.io](), to use it, add the following to y ```toml [dependencies] -rakrs = "0.1.0" +rak-rs = "0.1.0" ``` ## Features @@ -19,7 +19,7 @@ If you wish to use these features, add them to your `Cargo.toml` as seen below: ```toml [dependencies] -rak_rs = { version = "0.3.0", default-features = false, features = [ "async_tokio", "mcpe" ] } +rak-rs = { version = "0.1.0", default-features = false, features = [ "async_tokio", "mcpe" ] } ```