bancho_packets/
lib.rs

1#![allow(dead_code)]
2#![allow(non_camel_case_types)]
3#![allow(unused_parens)]
4#![allow(unused_mut)]
5
6#[cfg(test)]
7mod tests;
8
9/// Predefined client related bancho packets.
10pub mod client;
11/// Predefined server related bancho packets.
12pub mod server;
13
14#[cfg(feature = "derive")]
15pub use bancho_packets_derive::*;
16
17use enum_primitive_derive::Primitive;
18use std::{
19    borrow::Cow,
20    convert::TryInto,
21    ops::{Deref, DerefMut},
22};
23
24pub type CowStr<'a> = Cow<'a, str>;
25
26pub trait BanchoPacket {
27    const ID: PacketId;
28
29    fn into_packet_data(self) -> Vec<u8>;
30}
31
32/// Packet header length
33///
34/// - 0..=1: packet id
35/// - 2: null
36/// - 3..=6: packet length
37/// - 7..=N: packet data length(uleb128) + data
38pub const BANCHO_PACKET_HEADER_LENGTH: usize = 7;
39
40pub const EMPTY_STRING_PACKET: &[u8; 2] = b"\x0b\x00";
41
42#[cfg(feature = "serde")]
43use serde::{Deserialize, Serialize};
44
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[derive(Debug, Clone)]
47/// Bancho packet, including [`PacketHeader`] structure and payload bytes.
48pub struct Packet<'a> {
49    pub id: PacketId,
50    pub payload: Option<&'a [u8]>,
51}
52
53impl<'a> Packet<'a> {
54    pub fn new(id: PacketId) -> Self {
55        Self { id, payload: None }
56    }
57
58    pub fn with_payload(id: PacketId, payload: Option<&'a [u8]>) -> Self {
59        Self { id, payload }
60    }
61
62    pub fn with_raw_id_and_payload(
63        raw_id: u8,
64        payload: Option<&'a [u8]>,
65    ) -> Self {
66        Self { id: PacketId::new(raw_id), payload }
67    }
68}
69
70impl<'a> std::fmt::Display for Packet<'a> {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        f.write_fmt(format_args!(
73            "Packet {{ {}, payload: {} }}",
74            self.id,
75            self.payload.is_some()
76        ))
77    }
78}
79
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81#[derive(Debug, Clone)]
82/// Bancho packet header, including [`PacketId`] and payload (data) length.
83pub struct PacketHeader {
84    pub id: PacketId,
85    pub payload_length: u32,
86}
87
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89#[derive(Debug, Clone)]
90/// The login result of the bancho client.
91/// Returns `Success(user_id)` if success.
92pub enum LoginResult {
93    Success(i32),
94    Failed(LoginFailedReason),
95}
96
97impl Default for LoginResult {
98    fn default() -> Self {
99        Self::Failed(Default::default())
100    }
101}
102
103impl From<i32> for LoginResult {
104    fn from(val: i32) -> Self {
105        Self::Success(val)
106    }
107}
108
109impl From<LoginFailedReason> for LoginResult {
110    fn from(val: LoginFailedReason) -> Self {
111        Self::Failed(val)
112    }
113}
114
115#[rustfmt::skip]
116#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
117#[derive(
118    Debug,
119    Default,
120    Clone,
121    Copy,
122    PartialEq,
123    Eq,
124    PartialOrd,
125    Ord,
126    Hash
127)]
128#[repr(i32)]
129/// The bancho client will handle these failure reasons when the user login fails.
130pub enum LoginFailedReason {
131    #[default]
132    InvalidCredentials        = -1,
133    OutdatedClient            = -2,
134    UserBanned                = -3,
135    MultiaccountDetected      = -4,
136    ServerError               = -5,
137    CuttingEdgeMultiplayer    = -6,
138    AccountPasswordRest       = -7,
139    VerificationRequired      = -8,
140}
141
142#[rustfmt::skip]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144#[derive(
145    Debug,
146    Default,
147    Clone,
148    Copy,
149    PartialEq,
150    Eq,
151    PartialOrd,
152    Ord,
153    Hash,
154    Primitive
155)]
156#[repr(u8)]
157/// Known packet ids for bancho clients.
158pub enum PacketId {
159    OSU_USER_CHANGE_ACTION                = 0,
160    OSU_SEND_PUBLIC_MESSAGE               = 1,
161    OSU_USER_LOGOUT                       = 2,
162    OSU_USER_REQUEST_STATUS_UPDATE        = 3,
163    OSU_PING                              = 4,
164    BANCHO_USER_LOGIN_REPLY               = 5,
165    BANCHO_SEND_MESSAGE                   = 7,
166    BANCHO_PONG                           = 8,
167    BANCHO_HANDLE_IRC_CHANGE_USERNAME     = 9,
168    BANCHO_HANDLE_IRC_QUIT                = 10,
169    BANCHO_USER_STATS                     = 11,
170    BANCHO_USER_LOGOUT                    = 12,
171    BANCHO_SPECTATOR_JOINED               = 13,
172    BANCHO_SPECTATOR_LEFT                 = 14,
173    BANCHO_SPECTATE_FRAMES                = 15,
174    OSU_SPECTATE_START                    = 16,
175    OSU_SPECTATE_STOP                     = 17,
176    OSU_SPECTATE_FRAMES                   = 18,
177    BANCHO_VERSION_UPDATE                 = 19,
178    OSU_ERROR_REPORT                      = 20,
179    OSU_SPECTATE_CANT                     = 21,
180    BANCHO_SPECTATOR_CANT_SPECTATE        = 22,
181    BANCHO_GET_ATTENTION                  = 23,
182    BANCHO_NOTIFICATION                   = 24,
183    OSU_SEND_PRIVATE_MESSAGE              = 25,
184    BANCHO_UPDATE_MATCH                   = 26,
185    BANCHO_NEW_MATCH                      = 27,
186    BANCHO_DISBAND_MATCH                  = 28,
187    OSU_USER_PART_LOBBY                   = 29,
188    OSU_USER_JOIN_LOBBY                   = 30,
189    OSU_USER_CREATE_MATCH                 = 31,
190    OSU_USER_JOIN_MATCH                   = 32,
191    OSU_USER_PART_MATCH                   = 33,
192    BANCHO_TOGGLE_BLOCK_NON_FRIEND_DMS    = 34,
193    BANCHO_MATCH_JOIN_SUCCESS             = 36,
194    BANCHO_MATCH_JOIN_FAIL                = 37,
195    OSU_MATCH_CHANGE_SLOT                 = 38,
196    OSU_USER_MATCH_READY                  = 39,
197    OSU_MATCH_LOCK                        = 40,
198    OSU_MATCH_CHANGE_SETTINGS             = 41,
199    BANCHO_FELLOW_SPECTATOR_JOINED        = 42,
200    BANCHO_FELLOW_SPECTATOR_LEFT          = 43,
201    OSU_MATCH_START                       = 44,
202    BANCHO_ALL_PLAYERS_LOADED             = 45,
203    BANCHO_MATCH_START                    = 46,
204    OSU_MATCH_SCORE_UPDATE                = 47,
205    BANCHO_MATCH_SCORE_UPDATE             = 48,
206    OSU_MATCH_COMPLETE                    = 49,
207    BANCHO_MATCH_TRANSFER_HOST            = 50,
208    OSU_MATCH_CHANGE_MODS                 = 51,
209    OSU_MATCH_LOAD_COMPLETE               = 52,
210    BANCHO_MATCH_ALL_PLAYERS_LOADED       = 53,
211    OSU_MATCH_NO_BEATMAP                  = 54,
212    OSU_MATCH_NOT_READY                   = 55,
213    OSU_MATCH_FAILED                      = 56,
214    BANCHO_MATCH_PLAYER_FAILED            = 57,
215    BANCHO_MATCH_COMPLETE                 = 58,
216    OSU_MATCH_HAS_BEATMAP                 = 59,
217    OSU_MATCH_SKIP_REQUEST                = 60,
218    BANCHO_MATCH_SKIP                     = 61,
219    BANCHO_UNAUTHORIZED                   = 62,
220    OSU_USER_CHANNEL_JOIN                 = 63,
221    BANCHO_CHANNEL_JOIN_SUCCESS           = 64,
222    BANCHO_CHANNEL_INFO                   = 65,
223    BANCHO_CHANNEL_KICK                   = 66,
224    BANCHO_CHANNEL_AUTO_JOIN              = 67,
225    OSU_BEATMAP_INFO_REQUEST              = 68,
226    BANCHO_BEATMAP_INFO_REPLY             = 69,
227    OSU_MATCH_TRANSFER_HOST               = 70,
228    BANCHO_PRIVILEGES                     = 71,
229    BANCHO_FRIENDS_LIST                   = 72,
230    OSU_USER_FRIEND_ADD                   = 73,
231    OSU_USER_FRIEND_REMOVE                = 74,
232    BANCHO_PROTOCOL_VERSION               = 75,
233    BANCHO_MAIN_MENU_ICON                 = 76,
234    OSU_MATCH_CHANGE_TEAM                 = 77,
235    OSU_USER_CHANNEL_PART                 = 78,
236    OSU_USER_RECEIVE_UPDATES              = 79,
237    BANCHO_MONITOR                        = 80,
238    BANCHO_MATCH_PLAYER_SKIPPED           = 81,
239    OSU_USER_SET_AWAY_MESSAGE             = 82,
240    BANCHO_USER_PRESENCE                  = 83,
241    OSU_IRC_ONLY                          = 84,
242    OSU_USER_STATS_REQUEST                = 85,
243    BANCHO_RESTART                        = 86,
244    OSU_MATCH_INVITE                      = 87,
245    BANCHO_MATCH_INVITE                   = 88,
246    BANCHO_CHANNEL_INFO_END               = 89,
247    OSU_MATCH_CHANGE_PASSWORD             = 90,
248    BANCHO_MATCH_CHANGE_PASSWORD          = 91,
249    BANCHO_SILENCE_END                    = 92,
250    OSU_TOURNAMENT_MATCH_INFO_REQUEST     = 93,
251    BANCHO_USER_SILENCED                  = 94,
252    BANCHO_USER_PRESENCE_SINGLE           = 95,
253    BANCHO_USER_PRESENCE_BUNDLE           = 96,
254    OSU_USER_PRESENCE_REQUEST             = 97,
255    OSU_USER_PRESENCE_REQUEST_ALL         = 98,
256    OSU_USER_TOGGLE_BLOCK_NON_FRIEND_DMS  = 99,
257    BANCHO_USER_DM_BLOCKED                = 100,
258    BANCHO_TARGET_IS_SILENCED             = 101,
259    BANCHO_VERSION_UPDATE_FORCED          = 102,
260    BANCHO_SWITCH_SERVER                  = 103,
261    BANCHO_ACCOUNT_RESTRICTED             = 104,
262    BANCHO_RTX                            = 105,
263    BANCHO_MATCH_ABORT                    = 106,
264    BANCHO_SWITCH_TOURNAMENT_SERVER       = 107,
265    OSU_TOURNAMENT_JOIN_MATCH_CHANNEL     = 108,
266    OSU_TOURNAMENT_LEAVE_MATCH_CHANNEL    = 109,
267    #[default]
268    OSU_UNKNOWN_PACKET                    = 255,
269}
270
271impl std::fmt::Display for PacketId {
272    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273        f.write_fmt(format_args!("{:?}", self))
274    }
275}
276
277impl PacketId {
278    pub fn new(value: u8) -> Self {
279        Self::try_from(value).unwrap_or_default()
280    }
281}
282
283#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
284#[derive(Debug, Clone, ReadPacket, Default)]
285/// [`BanchoMessage`] is the message structure of the bancho client.
286pub struct BanchoMessage {
287    pub sender: String,
288    pub content: String,
289    pub target: String,
290    pub sender_id: i32,
291}
292
293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294#[derive(Debug, Clone, PacketLength, Default)]
295/// [`MatchData`] is the data of bancho client multiplayer game room.
296pub struct MatchData {
297    pub match_id: i32,
298    pub in_progress: bool,
299    pub match_type: i8,
300    pub play_mods: u32,
301    pub match_name: String,
302    #[length(self.password.as_ref().map(|pw| pw.packet_len()).unwrap_or(2))]
303    pub password: Option<String>,
304    pub beatmap_name: String,
305    pub beatmap_id: i32,
306    pub beatmap_md5: String,
307    pub slot_status: Vec<u8>,
308    pub slot_teams: Vec<u8>,
309    pub slot_players: Vec<i32>,
310    pub host_player_id: i32,
311    pub match_game_mode: u8,
312    pub win_condition: u8,
313    pub team_type: u8,
314    pub freemods: bool,
315    pub player_mods: Vec<i32>,
316    pub match_seed: i32,
317}
318
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[derive(Debug, Clone, PacketLength, Default)]
321pub struct MatchUpdate {
322    pub data: MatchData,
323    pub send_password: bool,
324}
325
326impl Deref for MatchUpdate {
327    type Target = MatchData;
328
329    #[inline]
330    fn deref(&self) -> &Self::Target {
331        &self.data
332    }
333}
334
335impl DerefMut for MatchUpdate {
336    #[inline]
337    fn deref_mut(&mut self) -> &mut Self::Target {
338        &mut self.data
339    }
340}
341
342#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
343#[derive(Debug, Clone, ReadPacket, WritePacket, PacketLength, Default)]
344/// The [`ScoreFrame`] uploaded by the bancho client during multiplayer games.
345pub struct ScoreFrame {
346    pub timestamp: i32,
347    pub id: u8,
348    pub n300: u16,
349    pub n100: u16,
350    pub n50: u16,
351    pub geki: u16,
352    pub katu: u16,
353    pub miss: u16,
354    pub score: i32,
355    pub combo: u16,
356    pub max_combo: u16,
357    pub perfect: bool,
358    pub hp: u8,
359    pub tag_byte: u8,
360    pub score_v2: bool,
361}
362
363#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
364#[derive(Debug, Clone, ReadPacket, WritePacket, PacketLength, Default)]
365pub struct ClientChangeAction {
366    pub online_status: u8,
367    pub description: String,
368    pub beatmap_md5: String,
369    pub mods: u32,
370    pub mode: u8,
371    pub beatmap_id: i32,
372}
373
374#[derive(Debug, Clone)]
375/// [`PayloadReader`] helps to read Bacho packet data.
376///
377/// ### Usage:
378/// ```
379/// use bancho_packets::{PacketReader, PayloadReader};
380///
381///
382/// let mut reader = PacketReader::new(&[
383///     4, 0, 0, 0, 0, 0, 0, 24, 0, 0, 19, 0, 0, 0, 11, 17, 72, 101, 108,
384///     108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 240, 159, 146, 150,
385///     4, 0, 0, 0, 0, 0, 0, 24, 0, 0, 18, 0, 0, 0, 11, 16, 229, 147, 136,
386///     229, 147, 136, 227, 128, 144, 240, 159, 152, 131, 227, 128, 145,
387///     104, 0, 0, 0, 0, 0, 0, 24, 0, 0, 23, 0, 0, 0, 11, 21, 232, 175,
388///     187, 229, 143, 150, 229, 174, 140, 228, 186, 134, 239, 188, 129,
389///     239, 188, 129, 226, 156, 168,
390/// ]);
391/// while let Some(packet) = reader.next() {
392///     print!("packet id: {:?}: ", packet.id);
393///     match packet.payload {
394///         None => println!("Non-payload"),
395///         Some(payload) => {
396///             let mut payload_reader = PayloadReader::new(payload);
397///             println!("payload as string: {:?}", payload_reader.read::<String>());
398///         },
399///     }
400/// }
401/// ```
402pub struct PayloadReader<'a> {
403    pub(crate) payload: &'a [u8],
404    pub(crate) index: usize,
405}
406
407impl<'a> PayloadReader<'a> {
408    #[inline]
409    pub fn new(payload: &'a [u8]) -> Self {
410        PayloadReader { payload, index: 0 }
411    }
412
413    #[inline]
414    /// Try to read `<T>` from payloads.
415    /// Returns [`None`] when no data matching the given type can be read.
416    pub fn read<T>(&mut self) -> Option<T>
417    where
418        T: BanchoPacketRead<T>,
419    {
420        T::read(self)
421    }
422
423    #[inline]
424    pub fn index(&self) -> usize {
425        self.index
426    }
427
428    #[inline]
429    pub fn payload(&self) -> &'a [u8] {
430        self.payload
431    }
432
433    #[inline]
434    /// Reset read progress to `0`.
435    pub fn reset(&mut self) {
436        self.index = 0
437    }
438
439    #[inline]
440    pub(crate) fn increase_index(&mut self, offset: usize) -> usize {
441        self.index += offset;
442        self.index
443    }
444
445    #[inline]
446    pub(crate) fn decrease_index(&mut self, offset: usize) -> usize {
447        self.index -= offset;
448        self.index
449    }
450
451    #[inline]
452    pub(crate) fn next_with_length(&mut self, length: usize) -> Option<&[u8]> {
453        self.index += length;
454        self.payload.get(self.index - length..self.index)
455    }
456
457    #[inline]
458    pub(crate) fn next_with_length_type<Len: Sized>(
459        &mut self,
460    ) -> Option<&[u8]> {
461        self.next_with_length(std::mem::size_of::<Len>())
462    }
463
464    #[inline]
465    pub(crate) fn read_uleb128(&mut self) -> Option<u32> {
466        let (val, length) = uleb128_to_u32(self.payload.get(self.index..)?)?;
467        self.index += length;
468        Some(val)
469    }
470}
471
472#[derive(Debug, Clone)]
473/// [`PacketReader`] helps to read Bacho packets.
474///
475/// ### Usage:
476/// ```
477/// use bancho_packets::{PacketReader, PayloadReader};
478///
479/// // Parsing [`PacketHeader`] from bytes.
480/// let header = PacketReader::parse_header(&[
481///     24, 0, 0, 7, 0, 0, 0, 11, 5, 104, 101, 108, 108, 111
482/// ]);
483///
484/// // Read sequentially.
485/// let mut reader = PacketReader::new(&[
486///     24, 0, 0, 7, 0, 0, 0, 11, 5, 104, 101, 108, 108, 111, 4, 0, 0, 0,
487///     0, 0, 0, 24, 0, 0, 7, 0, 0, 0, 11, 5, 104, 101, 108, 108, 111,
488/// ]);
489/// println!("packet 0: {:?}", reader.next());
490/// println!("packet 1: {:?}", reader.next());
491/// println!("packet 2: {:?}", reader.next());
492/// println!("packet 3 (outside): {:?}", reader.next());
493///
494/// // Full example.
495/// let mut reader = PacketReader::new(&[
496///     4, 0, 0, 0, 0, 0, 0, 24, 0, 0, 19, 0, 0, 0, 11, 17, 72, 101, 108,
497///     108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 240, 159, 146, 150,
498///     4, 0, 0, 0, 0, 0, 0, 24, 0, 0, 18, 0, 0, 0, 11, 16, 229, 147, 136,
499///     229, 147, 136, 227, 128, 144, 240, 159, 152, 131, 227, 128, 145,
500///     104, 0, 0, 0, 0, 0, 0, 24, 0, 0, 23, 0, 0, 0, 11, 21, 232, 175,
501///     187, 229, 143, 150, 229, 174, 140, 228, 186, 134, 239, 188, 129,
502///     239, 188, 129, 226, 156, 168,
503/// ]);
504/// while let Some(packet) = reader.next() {
505///     print!("packet id: {:?}: ", packet.id);
506///     match packet.payload {
507///         None => println!("Non-payload"),
508///         Some(payload) => {
509///             let mut payload_reader = PayloadReader::new(payload);
510///             println!("payload as string: {:?}", payload_reader.read::<String>());
511///         },
512///     }
513/// }
514/// ```
515pub struct PacketReader<'a> {
516    buf: &'a [u8],
517    ptr: usize,
518}
519
520impl<'a> PacketReader<'a> {
521    #[inline]
522    /// Create a new [`PacketReader`] with bytes.
523    pub fn new(buf: &'a [u8]) -> Self {
524        PacketReader { buf, ptr: 0 }
525    }
526
527    #[inline]
528    pub fn index(&self) -> usize {
529        self.ptr
530    }
531
532    #[inline]
533    pub fn buffer(&self) -> &'a [u8] {
534        self.buf
535    }
536
537    #[inline]
538    /// Reset the reading progress of [`PacketReader`].
539    pub fn reset(&mut self) {
540        self.ptr = 0;
541    }
542
543    #[inline]
544    /// Parse [`PacketHeader`] from bytes.
545    pub fn parse_header(header: &[u8]) -> Option<PacketHeader> {
546        let packet_id = *header.first()?;
547        Some(PacketHeader {
548            id: PacketId::new(packet_id),
549            payload_length: u32::from_le_bytes(
550                header[3..BANCHO_PACKET_HEADER_LENGTH].try_into().ok()?,
551            ),
552        })
553    }
554}
555
556impl<'a> Iterator for PacketReader<'a> {
557    type Item = Packet<'a>;
558
559    fn next(&mut self) -> Option<Self::Item> {
560        if (self.buf.len() - self.ptr) < BANCHO_PACKET_HEADER_LENGTH {
561            return None;
562        }
563        // Slice packet header data `[u8; 7]`,
564        // including packet id, payload length
565        let header =
566            &self.buf[self.ptr..self.ptr + BANCHO_PACKET_HEADER_LENGTH];
567        self.ptr += BANCHO_PACKET_HEADER_LENGTH;
568
569        // Get packet id and payload length
570        let PacketHeader { id, payload_length } =
571            PacketReader::parse_header(header)?;
572
573        // Read the payload
574        let payload = if payload_length == 0 {
575            None
576        } else {
577            let next_payload_length = payload_length as usize;
578
579            self.ptr += next_payload_length;
580            self.buf.get(self.ptr - next_payload_length..self.ptr)
581        };
582
583        Some(Packet { id, payload })
584    }
585}
586
587#[derive(Debug, Clone)]
588/// [`PacketBuilder`] can help pack bancho packets.
589///
590/// ### Usages:
591/// ```
592/// use bancho_packets::*;
593///
594/// let packet = PacketBuilder::new()
595///     .add(server::LoginReply::pack(LoginResult::Success(1009)))
596///     .add(server::ProtocolVersion::pack(19))
597///     .add(server::Notification::pack("Welcome to osu!".into()))
598///     .add(server::MainMenuIcon::pack(
599///         "https://image.png".into(),
600///         "https://url.link".into(),
601///     ))
602///     .add(server::SilenceEnd::pack(0))
603///     .add(server::ChannelInfoEnd::pack())
604///     .build();
605/// ```
606pub struct PacketBuilder {
607    buffer: Vec<u8>,
608}
609
610impl Default for PacketBuilder {
611    fn default() -> Self {
612        Self::new()
613    }
614}
615
616impl Deref for PacketBuilder {
617    type Target = Vec<u8>;
618
619    fn deref(&self) -> &Self::Target {
620        &self.buffer
621    }
622}
623
624impl DerefMut for PacketBuilder {
625    fn deref_mut(&mut self) -> &mut Self::Target {
626        &mut self.buffer
627    }
628}
629
630impl PacketBuilder {
631    #[inline]
632    /// Create an empty [`PacketBuilder`].
633    pub fn new() -> Self {
634        Self { buffer: Vec::new() }
635    }
636
637    #[inline]
638    /// Create [`PacketBuilder`] with capacity.
639    pub fn with_capacity(capacity: usize) -> Self {
640        Self { buffer: Vec::with_capacity(capacity) }
641    }
642
643    #[inline]
644    /// Create [`PacketBuilder`] with [`PacketId`].
645    pub fn with_packet_id(packet_id: PacketId) -> Self {
646        Self { buffer: new_empty_packet(packet_id) }
647    }
648
649    #[inline]
650    /// Consume this [`PacketBuilder`] and return data.
651    pub fn build(self) -> Vec<u8> {
652        self.buffer
653    }
654
655    #[inline]
656    pub fn buffer(&self) -> &Vec<u8> {
657        &self.buffer
658    }
659
660    #[inline]
661    pub fn buffer_mut(&mut self) -> &mut Vec<u8> {
662        &mut self.buffer
663    }
664
665    #[inline]
666    /// Create a [`PacketBuilder`] from multiple packets.
667    pub fn from_batch<P, I>(packets: P) -> Self
668    where
669        I: IntoIterator<Item = u8>,
670        P: IntoIterator<Item = I>,
671    {
672        Self::new().add_batch(packets)
673    }
674
675    #[inline]
676    /// Batch add packets to [`PacketBuilder`].
677    pub fn add_batch<P, I>(mut self, packets: P) -> Self
678    where
679        I: IntoIterator<Item = u8>,
680        P: IntoIterator<Item = I>,
681    {
682        self.add_batch_ref(packets);
683        self
684    }
685
686    #[inline]
687    /// Batch add packets to [`PacketBuilder`].
688    pub fn add_batch_ref<P, I>(&mut self, packets: P) -> &Self
689    where
690        I: IntoIterator<Item = u8>,
691        P: IntoIterator<Item = I>,
692    {
693        for p in packets {
694            self.buffer.extend(p)
695        }
696        self
697    }
698
699    #[allow(clippy::should_implement_trait)]
700    #[inline]
701    /// Add an packet to [`PacketBuilder`].
702    pub fn add<P>(mut self, packet: P) -> Self
703    where
704        P: IntoIterator<Item = u8>,
705    {
706        self.add_ref(packet);
707        self
708    }
709
710    #[inline]
711    /// Add an packet to [`PacketBuilder`].
712    pub fn add_ref<P>(&mut self, packet: P) -> &Self
713    where
714        P: IntoIterator<Item = u8>,
715    {
716        self.buffer.extend(packet);
717        self
718    }
719}
720
721impl From<Vec<u8>> for PacketBuilder {
722    #[inline]
723    fn from(buffer: Vec<u8>) -> Self {
724        Self { buffer }
725    }
726}
727
728/// Can use [`PayloadReader`] to read data from type `T` which implements this
729/// trait.
730pub trait BanchoPacketRead<T> {
731    fn read(reader: &mut PayloadReader) -> Option<T>;
732}
733
734impl BanchoPacketRead<String> for String {
735    #[inline]
736    fn read(reader: &mut PayloadReader) -> Option<String> {
737        if reader.payload.get(reader.index())? != &0xb {
738            return None;
739        }
740        reader.increase_index(1);
741        let data_length = reader.read_uleb128()? as usize;
742
743        let cur = reader.index;
744        reader.increase_index(data_length);
745        let data = reader.payload.get(cur..reader.index)?;
746
747        Some(std::str::from_utf8(data).ok()?.into())
748    }
749}
750
751impl BanchoPacketRead<bool> for bool {
752    #[inline]
753    fn read(reader: &mut PayloadReader) -> Option<bool> {
754        Some(reader.read::<i8>()? == 1)
755    }
756}
757
758macro_rules! impl_number {
759    ($($t:ty),+) => {
760        $(impl BanchoPacketRead<$t> for $t {
761            #[inline]
762            fn read(reader: &mut PayloadReader) -> Option<$t> {
763                Some(<$t>::from_le_bytes(
764                    reader.next_with_length_type::<$t>()?.try_into().ok()?,
765                ))
766            }
767        })+
768    };
769}
770
771impl_number!(i8, u8, i16, u16, i32, u32, i64, u64);
772
773macro_rules! impl_read_number_array {
774    ($($t:ty),+) => {
775        $(impl BanchoPacketRead<Vec<$t>> for Vec<$t> {
776            #[inline]
777            fn read(reader: &mut PayloadReader) -> Option<Vec<$t>> {
778                let length_data = reader.next_with_length_type::<i16>()?;
779                let int_count = <i16>::from_le_bytes(length_data.try_into().ok()?) as usize;
780
781                let mut data = Vec::with_capacity(int_count);
782                for _ in 0..int_count {
783                    data.push(<$t>::from_le_bytes(reader.next_with_length_type::<i32>()?.try_into().ok()?));
784                }
785                Some(data)
786            }
787        })+
788    };
789}
790
791impl_read_number_array!(i8, u8, i16, u16, i32, u32, i64, u64);
792
793/// [`BanchoPacketWrite`] is a trait used to convert rust internal data types to
794/// bancho packets ([`Vec<u8>`]).
795pub trait BanchoPacketWrite {
796    /// Convert [`self`] into a bancho packet and write it into `buf`
797    /// [`Vec<u8>`].
798    fn write_into_buf(self, buf: &mut Vec<u8>);
799
800    #[inline]
801    /// Convert [`self`] into a bancho packet [`Vec<u8>`].
802    fn into_packet(self) -> Vec<u8>
803    where
804        Self: Sized,
805    {
806        let mut buf = Vec::new();
807        self.write_into_buf(&mut buf);
808        buf
809    }
810}
811
812impl BanchoPacketWrite for Cow<'_, str> {
813    #[inline]
814    fn write_into_buf(self, buf: &mut Vec<u8>) {
815        match self {
816            Cow::Borrowed(s) => s.write_into_buf(buf),
817            Cow::Owned(s) => s.write_into_buf(buf),
818        }
819    }
820}
821
822impl BanchoPacketWrite for &str {
823    #[inline]
824    fn write_into_buf(self, buf: &mut Vec<u8>) {
825        let byte_length = self.len();
826        if byte_length > 0 {
827            // string length (uleb128)
828            let (data, ptr) = u32_to_uleb128(byte_length as u32);
829            let length_uleb128 = &data[0..ptr];
830
831            let estimate_len = self.packet_len();
832            if buf.capacity() < estimate_len {
833                buf.reserve(estimate_len);
834            }
835
836            buf.push(0xb);
837            buf.extend(length_uleb128);
838            buf.extend(self.as_bytes());
839        } else {
840            buf.push(0);
841        }
842    }
843}
844
845impl BanchoPacketWrite for String {
846    #[inline]
847    fn write_into_buf(self, buf: &mut Vec<u8>) {
848        let byte_length = self.len();
849        if byte_length > 0 {
850            // string length (uleb128)
851            let (data, ptr) = u32_to_uleb128(byte_length as u32);
852            let length_uleb128 = &data[0..ptr];
853
854            let estimate_len = self.packet_len();
855            if buf.capacity() < estimate_len {
856                buf.reserve(estimate_len);
857            }
858
859            buf.push(0xb);
860            buf.extend(length_uleb128);
861            buf.extend(self.into_bytes());
862        } else {
863            buf.push(0);
864        }
865    }
866}
867
868impl BanchoPacketWrite for u8 {
869    #[inline]
870    fn write_into_buf(self, buf: &mut Vec<u8>) {
871        buf.push(self);
872    }
873}
874
875impl BanchoPacketWrite for &[u8] {
876    #[inline]
877    fn write_into_buf(self, buf: &mut Vec<u8>) {
878        buf.extend(self);
879    }
880}
881
882impl BanchoPacketWrite for Vec<u8> {
883    #[inline]
884    fn write_into_buf(self, buf: &mut Vec<u8>) {
885        buf.extend(self);
886    }
887}
888
889impl BanchoPacketWrite for bool {
890    #[inline]
891    fn write_into_buf(self, buf: &mut Vec<u8>) {
892        buf.push(if self { 1 } else { 0 });
893    }
894}
895
896macro_rules! impl_write_number {
897    ($($t:ty),+) => {
898        $(
899            impl BanchoPacketWrite for $t {
900                #[inline]
901                fn write_into_buf(self, buf: &mut Vec<u8>) {
902                    buf.extend(self.to_le_bytes())
903                }
904            }
905
906            impl BanchoPacketLength for $t {
907                #[inline]
908                fn packet_len(&self) -> usize {
909                    std::mem::size_of::<$t>()
910                }
911            }
912        )+
913    }
914}
915
916impl_write_number!(i8, u16, i16, i32, u32, i64, u64, f32, f64);
917
918macro_rules! impl_write_number_array {
919    ($($t:ty),+) => {$(
920        impl BanchoPacketWrite for &[$t] { impl_write_number_array!(@bancho_packet_write_inner $t); }
921        impl BanchoPacketWrite for Vec<$t> { impl_write_number_array!(@bancho_packet_write_inner $t); }
922
923        impl BanchoPacketLength for &[$t] { impl_write_number_array!(@bancho_packet_length_inner $t); }
924        impl BanchoPacketLength for Vec<$t> { impl_write_number_array!(@bancho_packet_length_inner $t); }
925    )+};
926    (@bancho_packet_write_inner $t:ty) => {
927        #[inline]
928        fn write_into_buf(self, buf: &mut Vec<u8>) {
929            let estimate_len = self.packet_len();
930            if buf.capacity() < estimate_len {
931                buf.reserve(estimate_len);
932            }
933
934            buf.extend((self.len() as u16).to_le_bytes());
935            for int in self {
936                buf.extend(int.to_le_bytes())
937            }
938        }
939    };
940    (@bancho_packet_length_inner $t:ty) => {
941        #[allow(clippy::manual_slice_size_calculation)]
942        #[inline]
943        fn packet_len(&self) -> usize {
944            std::mem::size_of::<u16>() + (std::mem::size_of::<$t>() * self.len())
945        }
946    }
947}
948
949impl_write_number_array!(i8, u16, i16, i32, u32, i64, u64, f32, f64);
950
951impl BanchoPacketWrite for LoginResult {
952    #[inline]
953    fn write_into_buf(self, buf: &mut Vec<u8>) {
954        match self {
955            LoginResult::Success(user_id) => user_id,
956            LoginResult::Failed(reason) => reason as i32,
957        }
958        .write_into_buf(buf)
959    }
960}
961
962impl BanchoPacketWrite for MatchUpdate {
963    #[inline]
964    fn write_into_buf(mut self, buf: &mut Vec<u8>) {
965        let MatchUpdate {
966            data:
967                MatchData {
968                    match_id,
969                    in_progress,
970                    match_type,
971                    play_mods,
972                    match_name,
973                    password,
974                    beatmap_name,
975                    beatmap_id,
976                    beatmap_md5,
977                    slot_status,
978                    slot_teams,
979                    slot_players,
980                    host_player_id,
981                    match_game_mode,
982                    win_condition,
983                    team_type,
984                    freemods,
985                    player_mods,
986                    match_seed,
987                },
988            send_password,
989        } = self;
990
991        let raw_password = password
992            .map(|password| {
993                if send_password {
994                    let mut raw = Vec::with_capacity(password.packet_len());
995                    password.write_into_buf(&mut raw);
996                    raw
997                } else {
998                    EMPTY_STRING_PACKET.to_vec()
999                }
1000            })
1001            .unwrap_or(b"\x00".to_vec());
1002
1003        buf.extend(data!(
1004            match_id as u16,
1005            in_progress,
1006            match_type,
1007            play_mods,
1008            match_name,
1009            raw_password,
1010            beatmap_name,
1011            beatmap_id,
1012            beatmap_md5,
1013            slot_status,
1014            slot_teams,
1015            slot_players,
1016            host_player_id,
1017            match_game_mode,
1018            win_condition,
1019            team_type,
1020            freemods,
1021            player_mods,
1022            match_seed
1023        ));
1024    }
1025}
1026
1027impl BanchoPacketWrite for MatchData {
1028    #[inline]
1029    fn write_into_buf(self, buf: &mut Vec<u8>) {
1030        MatchUpdate { data: self, send_password: true }.write_into_buf(buf);
1031    }
1032}
1033
1034/// [`BanchoPacketLength`] is a trait used to calculate the byte length of the
1035/// data converted to bancho packet.
1036pub trait BanchoPacketLength {
1037    #[inline]
1038    /// Calculate the byte length of `self` after being converted into a bancho
1039    /// packet, which is used to allocate [`Vec`] space in advance to
1040    /// improve performance.
1041    ///
1042    /// If not implemented, return `0`.
1043    fn packet_len(&self) -> usize {
1044        0
1045    }
1046}
1047
1048impl BanchoPacketLength for Cow<'_, str> {
1049    #[inline]
1050    fn packet_len(&self) -> usize {
1051        match self {
1052            Cow::Borrowed(s) => s.packet_len(),
1053            Cow::Owned(s) => s.packet_len(),
1054        }
1055    }
1056}
1057
1058impl BanchoPacketLength for &str {
1059    #[inline]
1060    fn packet_len(&self) -> usize {
1061        if !self.is_empty() {
1062            self.as_bytes().len() + 6
1063        } else {
1064            1
1065        }
1066    }
1067}
1068
1069impl BanchoPacketLength for String {
1070    #[inline]
1071    fn packet_len(&self) -> usize {
1072        if !self.is_empty() {
1073            self.as_bytes().len() + 6
1074        } else {
1075            1
1076        }
1077    }
1078}
1079
1080impl BanchoPacketLength for u8 {
1081    #[inline]
1082    fn packet_len(&self) -> usize {
1083        std::mem::size_of::<u8>()
1084    }
1085}
1086
1087impl BanchoPacketLength for &[u8] {
1088    #[inline]
1089    fn packet_len(&self) -> usize {
1090        self.len()
1091    }
1092}
1093
1094impl BanchoPacketLength for Vec<u8> {
1095    #[inline]
1096    fn packet_len(&self) -> usize {
1097        self.len()
1098    }
1099}
1100
1101impl BanchoPacketLength for bool {
1102    #[inline]
1103    fn packet_len(&self) -> usize {
1104        std::mem::size_of::<bool>()
1105    }
1106}
1107
1108impl BanchoPacketLength for LoginResult {
1109    #[inline]
1110    fn packet_len(&self) -> usize {
1111        std::mem::size_of::<i32>()
1112    }
1113}
1114
1115impl<T> BanchoPacketLength for Option<T>
1116where
1117    T: BanchoPacketLength,
1118{
1119    #[inline]
1120    fn packet_len(&self) -> usize {
1121        self.as_ref().map(|t| t.packet_len()).unwrap_or(0)
1122    }
1123}
1124
1125#[inline]
1126/// Convert [`u32`] to `uleb128`
1127pub fn u32_to_uleb128(mut unsigned: u32) -> ([u8; 5], usize) {
1128    let mut data = [0, 0, 0, 0, 0];
1129    let mut ptr = 0;
1130
1131    loop {
1132        if unsigned < 0x80 {
1133            break;
1134        }
1135        data[ptr] = ((unsigned & 0x7f) | 0x80) as u8;
1136        ptr += 1;
1137        unsigned >>= 7;
1138    }
1139    data[ptr] = unsigned as u8;
1140
1141    (data, ptr + 1)
1142}
1143
1144#[inline]
1145/// Convert `uleb128` bytes to [`u32`]
1146pub fn uleb128_to_u32(uleb128_bytes: &[u8]) -> Option<(u32, usize)> {
1147    let (mut val, mut shift, mut index) = (0, 0, 0);
1148    loop {
1149        let byte = uleb128_bytes.get(index)?;
1150        index += 1;
1151        if (byte & 0x80) == 0 {
1152            val |= (*byte as u32) << shift;
1153            return Some((val, index));
1154        }
1155        val |= ((byte & 0x7f) as u32) << shift;
1156        shift += 7;
1157    }
1158}
1159
1160#[inline(always)]
1161/// Initial a packet with PacketId
1162///
1163/// Packets posits:
1164///
1165/// - 0..=1: [`PacketId`]
1166/// - 2: `0x0`
1167/// - 3..=6: packet length ([`i32`] as bytes)
1168/// - 7..=N: packet data length (`uleb128` as bytes) + data
1169///
1170/// Note: The maximum value of [`u8`] is `255`,
1171/// currently the largest packet id of bancho is `109`, so never overflow.
1172pub fn new_empty_packet(packet_id: PacketId) -> Vec<u8> {
1173    vec![packet_id as u8, 0, 0, 0, 0, 0, 0]
1174}
1175
1176/// Provide some convenient declarative macros to help build bancho packets.
1177pub mod macros {
1178    #[macro_export]
1179    /// Pack bancho packet data
1180    ///
1181    /// ### Usages:
1182    /// ```
1183    /// use bancho_packets::*;
1184    ///
1185    /// let val_1: i32 = 123;
1186    /// let val_2: i16 = 50;
1187    ///
1188    /// // Single data
1189    /// data!(val_1);
1190    ///
1191    /// // Mutiple data
1192    /// data!(val_1, val_2);
1193    ///
1194    /// // Create packet data with additional capacity
1195    /// data!(@capacity { 100 }, val_1, val_2);
1196    /// ```
1197    macro_rules! data {
1198        ($($item:expr$(,)*)*) => {
1199            {
1200                let mut estimate_capacity = 0;
1201                $(estimate_capacity += $item.packet_len();)*
1202
1203                let mut buf = Vec::<u8>::with_capacity(estimate_capacity);
1204                $($item.write_into_buf(&mut buf);)*
1205                buf
1206            }
1207        };
1208        (@capacity { $capacity:expr }, $($item:expr$(,)*)*) => {
1209            {
1210                let mut estimate_capacity = 0;
1211                $(estimate_capacity += $item.packet_len();)*
1212
1213                let mut buf = Vec::<u8>::with_capacity($capacity + estimate_capacity);
1214                $($item.write_into_buf(&mut buf);)*
1215                buf
1216            }
1217        }
1218    }
1219
1220    #[macro_export]
1221    /// Pack bancho packets
1222    ///
1223    ///
1224    /// ### Usages:
1225    /// ```
1226    /// use bancho_packets::*;
1227    ///
1228    /// // Basic
1229    /// packet!(PacketId::BANCHO_USER_STATS);
1230    ///
1231    /// // With data
1232    /// let data: i32 = 6;
1233    /// packet!(PacketId::BANCHO_USER_STATS, data);
1234    ///
1235    /// // With data
1236    /// let data = vec![1, 2, 3];
1237    /// packet!(PacketId::BANCHO_USER_STATS, data);
1238    ///
1239    ///
1240    /// // With complex data
1241    /// let user_id: i32 = 1000;
1242    /// let username: &str = "username";
1243    ///
1244    /// packet!(
1245    ///     PacketId::BANCHO_USER_PRESENCE,
1246    ///     data!(
1247    ///         user_id,
1248    ///         username
1249    ///     )
1250    /// );
1251    /// ```
1252    macro_rules! packet {
1253        ($packet_id:expr $(,$data:expr)*) => {
1254            {
1255                let mut estimate_capacity = 0;
1256                $(estimate_capacity += $data.packet_len();)*
1257
1258                let mut packet = Vec::<u8>::with_capacity(estimate_capacity);
1259                packet.extend(&[$packet_id as u8, 0, 0, 0, 0, 0, 0]);
1260
1261                $($data.write_into_buf(&mut packet);)*
1262
1263                let packet_length_bytes =
1264                    ((packet.len() - $crate::BANCHO_PACKET_HEADER_LENGTH) as i32)
1265                        .to_le_bytes();
1266
1267                // `new_empty_packet` always returns a vector of length 7, there will be no null pointer.
1268                packet[3] = packet_length_bytes[0];
1269                packet[4] = packet_length_bytes[1];
1270                packet[5] = packet_length_bytes[2];
1271                packet[6] = packet_length_bytes[3];
1272
1273                packet
1274            }
1275        }
1276    }
1277
1278    #[macro_export]
1279    /// Impl bancho packet
1280    ///
1281    /// Example
1282    /// ```rust
1283    /// use bancho_packets::*;
1284    ///
1285    /// packet_struct!(
1286    ///     PacketId::OSU_USER_CHANGE_ACTION,
1287    ///     /// #0: OSU_USER_CHANGE_ACTION
1288    ///     UserChangeAction<'a> {
1289    ///         online_status: u8,
1290    ///         description: CowStr<'a>,
1291    ///         beatmap_md5: CowStr<'a>,
1292    ///         mods: u32,
1293    ///         mode: u8,
1294    ///         beatmap_id: i32,
1295    ///     }
1296    /// );
1297    /// ```
1298    macro_rules! packet_struct {
1299        (
1300            $packet_id: expr,
1301            $(#[$struct_meta:meta])*
1302            $struct_name:ident $(< $($lifetimes:lifetime),* >)? {
1303                $(
1304                    $(#[$field_meta:meta])*
1305                    $field_name:ident: $field_type:ty$(,)*
1306                )*
1307            },
1308            fn $fn:ident ($self:ident) -> $ret:ty $body:block
1309        ) => {
1310            $(#[$struct_meta])*
1311            #[derive(Debug, Clone, Default)]
1312            pub struct $struct_name $(< $($lifetimes),* >)? {
1313                $(
1314                    $(#[$field_meta])*
1315                    pub $field_name: $field_type,
1316                )*
1317            }
1318
1319            impl $(< $($lifetimes),* >)? $crate::BanchoPacketLength for $struct_name $(< $($lifetimes),* >)? {
1320                #[inline]
1321                fn packet_len(&self) -> usize {
1322                    let mut _len = $crate::BANCHO_PACKET_HEADER_LENGTH;
1323                    $(_len += self.$field_name.packet_len();)*
1324                    _len
1325                }
1326            }
1327
1328            impl$(< $($lifetimes),* >)? $struct_name$(< $($lifetimes),* >)? {
1329                #[allow(clippy::too_many_arguments)]
1330                #[inline]
1331                pub fn new(
1332                    $($field_name: $field_type,)*
1333                ) -> Self {
1334                    Self { $($field_name,)* }
1335                }
1336
1337                #[allow(clippy::too_many_arguments)]
1338                #[inline]
1339                pub fn pack(
1340                    $($field_name: $field_type,)*
1341                ) -> Vec<u8> {
1342                    $crate::BanchoPacket::into_packet_data(Self { $($field_name,)* })
1343                }
1344            }
1345
1346            impl$(< $($lifetimes),* >)? $crate::BanchoPacket for $struct_name$(< $($lifetimes),* >)? {
1347                const ID: $crate::PacketId = $packet_id;
1348
1349                #[inline]
1350                fn $fn ($self) -> $ret $body
1351            }
1352
1353            impl$(< $($lifetimes),* >)? IntoIterator for $struct_name$(< $($lifetimes),* >)? {
1354                type Item = u8;
1355
1356                type IntoIter = std::vec::IntoIter<u8>;
1357
1358                fn into_iter(self) -> Self::IntoIter {
1359                    $crate::BanchoPacket::into_packet_data(self).into_iter()
1360                }
1361            }
1362
1363            impl$(< $($lifetimes),* >)? From<$struct_name$(< $($lifetimes),* >)?> for Vec<u8> {
1364                fn from(packet: $struct_name$(< $($lifetimes),* >)?) -> Vec<u8> {
1365                    $crate::BanchoPacket::into_packet_data(packet)
1366                }
1367            }
1368        };
1369        (
1370            $packet_id: expr,
1371            $(#[$struct_meta:meta])*
1372            $struct_name:ident $(< $($lifetimes:lifetime),* >)? {
1373                $(
1374                    $(#[$field_meta:meta])*
1375                    $field_name:ident: $field_type:ty$(,)*
1376                )*
1377            }
1378        ) => {
1379            packet_struct!(
1380                $packet_id,
1381                $(#[$struct_meta])*
1382                $struct_name $(< $($lifetimes),* >)? {
1383                    $(
1384                        $(#[$field_meta])*
1385                        $field_name: $field_type,
1386                    )*
1387                },
1388                fn into_packet_data(self) -> Vec<u8> {
1389                    $crate::packet!(
1390                        Self::ID
1391                        $(,self.$field_name)*
1392                    )
1393                }
1394            );
1395        }
1396    }
1397}