Skip to main content

xuko_net/
packet.rs

1//! Generic packet format
2
3use serde::{Deserialize, Serialize};
4use std::{
5    io::{Read, Write},
6    marker::PhantomData,
7};
8use xuko_core::array::{Array, ArrayCreationError};
9
10#[cfg(feature = "async")]
11use smol::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
12
13const COMPRESSION_LEVEL: i32 = 3;
14
15// TODO: add UDP support?
16// UDP doesn't use the I/O `Write` and `Read` traits, hmmm
17
18/// Packet size
19pub type PacketSize = u64;
20
21/// Size of [`PacketSize`]
22pub const PACKET_SIZE: usize = std::mem::size_of::<PacketSize>();
23
24/// Max packet size, may be configured using [`set_max_packet_size`]
25static mut MAX_PACKET_SIZE: PacketSize = 2u64.pow(31) - 1;
26
27static mut PACKET_HAS_BEEN_CREATED: bool = false;
28
29/// Set the max packet size
30///
31/// This can only be before any [`Packet`] is created
32pub const fn set_max_packet_size(size: PacketSize) {
33    unsafe {
34        if !PACKET_HAS_BEEN_CREATED {
35            MAX_PACKET_SIZE = size;
36        }
37    }
38}
39
40/// Get the max packet size, it may be configured using [`set_max_packet_size`]
41pub const fn get_max_packet_size() -> PacketSize {
42    unsafe { MAX_PACKET_SIZE }
43}
44
45/// An error that occurs when using a [`Packet`]s functions
46#[derive(Debug, thiserror::Error)]
47pub enum PacketError {
48    /// Occurs when a packet is too large
49    #[error("packet is too large ({0} bytes)")]
50    TooLarge(usize),
51
52    /// Occurs when the amount of bytes read isn't what the reader expects
53    #[error("{context}; expected {expected} got {real}")]
54    LengthFailure {
55        /// Provides a little context on where this is happening
56        context: String,
57        /// The amount of bytes read expected
58        expected: usize,
59        /// The amount of bytes we actually read
60        real: usize,
61    },
62
63    /// [`ron`] error
64    #[error("(de)serialization error: {0}")]
65    RonError(#[from] ron::Error),
66
67    /// I/O Error
68    #[error("IO error: {0}")]
69    IOError(#[from] std::io::Error),
70
71    /// UTF-8 Error
72    #[error("invalid utf-8: {0}")]
73    UTF8Error(#[from] std::string::FromUtf8Error),
74
75    /// Array creation error
76    #[error("array creation error: {0}")]
77    ArrayCreationError(#[from] ArrayCreationError),
78}
79
80/// Generic packet format.
81///
82/// Useful for game servers and the like.
83///
84/// [`Packet`] supports writing to anything that implements [`Write`], and reading from
85/// anything that implements [`Read`]. including [`std::net::TcpStream`]s.
86///
87/// Currently it will not work with [`std::net::UdpSocket`] since it does not implement [`Write`] or
88/// [`Read`].
89///
90/// [`Packet`] supports a payload of any value that implements [`serde::Serialize`] and [`serde::Deserialize`].
91///
92/// The [`chomp_packet`] function allows you to read a packet from a [`Read`]er and will return a
93/// serialized string of the packet, use [`Packet::deserialize`] to deserialize it.
94///
95/// [`Packet::unwrap`] will give you the original payload stored in the packet.
96///
97/// # Examples
98///
99/// ```
100/// use serde::{Serialize, Deserialize};
101/// use std::io::Cursor;
102/// use xuko_net::packet::{Packet, chomp_packet};
103///
104/// #[derive(Serialize, Deserialize)]
105/// struct Payload {
106///     x: i32,
107///     y: f32
108/// }
109///
110/// let packet = Packet::new(Payload {
111///     x: 6,
112///     y: 3.14
113/// });
114///
115/// let mut stream = Cursor::new(Vec::new());
116///
117/// packet.send(&mut stream); // Write to a stream
118/// packet.unwrap(); // Unwrap the original value using `Packet::unwrap`
119/// ```
120pub struct Packet<'de, T>
121where
122    T: Serialize + Deserialize<'de>,
123{
124    payload: T,
125    _marker: PhantomData<&'de T>,
126}
127
128impl<'de, T: Serialize + Deserialize<'de>> Packet<'de, T> {
129    /// Create a new [Packet]
130    pub const fn new(payload: T) -> Self {
131        unsafe {
132            PACKET_HAS_BEEN_CREATED = true;
133        }
134
135        Self {
136            payload,
137            _marker: PhantomData,
138        }
139    }
140
141    /// Unwrap the [`Packet`] to its underlying type
142    pub fn unwrap(self) -> T {
143        self.payload
144    }
145
146    /// Serialize [`Packet`] into a [`String`]
147    pub fn serialize(&self) -> Result<String, ron::Error> {
148        ron::to_string(&self.payload)
149    }
150
151    /// Deserialize a string into a [`Packet`]
152    pub fn deserialize(serialized: &'de str) -> Result<Packet<'de, T>, ron::Error> {
153        Ok(Self::new(ron::from_str(serialized)?))
154    }
155
156    /// Write the [`Packet`] in its serialized form to a writer
157    pub fn send<W: Write>(&self, stream: &mut W) -> Result<(), PacketError> {
158        let packet = self.serialize()?;
159        let bytes = &zstd::encode_all(packet.as_bytes(), COMPRESSION_LEVEL)?;
160
161        if bytes.len() > get_max_packet_size() as usize {
162            return Err(PacketError::TooLarge(bytes.len()));
163        }
164
165        let packet_len = (bytes.len() as PacketSize).to_be_bytes();
166
167        stream.write_all(&packet_len)?;
168        stream.write_all(bytes)?;
169        stream.flush()?;
170
171        Ok(())
172    }
173
174    /// Same as [`Self::send`] but supports [`smol`]'s async I/O
175    #[cfg(feature = "async")]
176    pub async fn send_async<W: AsyncWrite + Unpin>(
177        &self,
178        stream: &mut W,
179    ) -> Result<(), PacketError> {
180        let packet = self.serialize()?;
181        let bytes = &zstd::encode_all(packet.as_bytes(), COMPRESSION_LEVEL)?;
182
183        if bytes.len() > unsafe { MAX_PACKET_SIZE } as usize {
184            return Err(PacketError::TooLarge(bytes.len()));
185        }
186
187        let packet_len = (bytes.len() as PacketSize).to_be_bytes();
188
189        stream.write_all(&packet_len).await?;
190        stream.write_all(bytes).await?;
191        stream.flush().await?;
192
193        Ok(())
194    }
195}
196
197/// Read a serialized [`Packet`] from a reader
198///
199/// # Note
200///
201/// This function does not care what the packet type is as it doesn't deserialize it.
202/// It should then be deserialized to a specific type using [`Packet::deserialize`].
203///
204/// The format of a packet looks like the following: (BBBBBBBB) *data*
205///
206/// The first 8 bytes tell us what the size of the is.
207/// The rest of the data is a generic packet serialized as a [`ron`] value.
208pub fn chomp_packet<R: Read>(stream: &mut R) -> Result<String, PacketError> {
209    let mut buf = [0u8; PACKET_SIZE];
210    let read = stream.read(&mut buf)?;
211
212    if read != PACKET_SIZE {
213        return Err(packet_size_error(PACKET_SIZE, read));
214    }
215
216    let len = PacketSize::from_be_bytes(buf) as usize;
217
218    let mut buf = Array::new(len)?;
219    let mut read = 0;
220
221    while let Ok(n) = stream.read(&mut buf) {
222        if n == 0 {
223            break;
224        }
225        read += n;
226    }
227
228    if read != len {
229        return Err(packet_bytes_read_error(len, read));
230    }
231
232    let b: &[u8] = &buf;
233    let serialized_packet = String::from_utf8(zstd::decode_all(b)?)?;
234
235    Ok(serialized_packet)
236}
237
238/// Same as [`chomp_packet`] but supports [`smol`]'s async I/O
239#[cfg(feature = "async")]
240pub async fn chomp_packet_async<R: AsyncRead + Unpin>(
241    stream: &mut R,
242) -> Result<String, PacketError> {
243    let mut buf = [0u8; PACKET_SIZE];
244    let read = stream.read(&mut buf).await?;
245
246    if read != PACKET_SIZE {
247        return Err(packet_size_error(PACKET_SIZE, read));
248    }
249
250    let len = PacketSize::from_be_bytes(buf) as usize;
251
252    let mut buf = Array::new(len)?;
253    let mut read = 0;
254
255    while let Ok(n) = stream.read(&mut buf).await {
256        if n == 0 {
257            break;
258        }
259        read += n;
260    }
261
262    if read != len {
263        return Err(packet_bytes_read_error(len, read));
264    }
265
266    let b: &[u8] = &buf;
267    let serialized_packet = String::from_utf8(zstd::decode_all(b)?)?;
268
269    Ok(serialized_packet)
270}
271
272fn packet_size_error(expected: usize, real: usize) -> PacketError {
273    PacketError::LengthFailure {
274        context: "invalid packet length".to_string(),
275        expected,
276        real,
277    }
278}
279
280fn packet_bytes_read_error(expected: usize, real: usize) -> PacketError {
281    PacketError::LengthFailure {
282        context: "didnt read expected amount of bytes from packet".to_string(),
283        expected,
284        real,
285    }
286}