#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(unused_parens)]
#![allow(unused_mut)]
#[cfg(test)]
mod tests;
pub mod client;
pub mod server;
#[cfg(feature = "derive")]
pub use bancho_packets_derive::*;
use enum_primitive_derive::Primitive;
use num_traits::FromPrimitive;
use std::{
borrow::Cow,
convert::TryInto,
ops::{Deref, DerefMut},
};
pub type CowStr<'a> = Cow<'a, str>;
pub trait BanchoPacket {
const ID: PacketId;
fn into_packet_data(self) -> Vec<u8>;
}
pub const BANCHO_PACKET_HEADER_LENGTH: usize = 7;
pub const EMPTY_STRING_PACKET: &[u8; 2] = b"\x0b\x00";
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Packet<'a> {
pub id: PacketId,
pub payload: Option<&'a [u8]>,
}
impl<'a> std::fmt::Display for Packet<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Packet {{ {}, payload: {} }}",
self.id,
self.payload.is_some()
))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct PacketHeader {
pub id: PacketId,
pub payload_length: u32,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub enum LoginResult {
Success(i32),
Failed(LoginFailedResaon),
}
impl Default for LoginResult {
fn default() -> Self {
Self::Failed(Default::default())
}
}
#[rustfmt::skip]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
#[repr(i32)]
pub enum LoginFailedResaon {
#[default]
InvalidCredentials = -1,
OutdatedClient = -2,
UserBanned = -3,
MultiaccountDetected = -4,
ServerError = -5,
CuttingEdgeMultiplayer = -6,
AccountPasswordRest = -7,
VerificationRequired = -8,
}
#[rustfmt::skip]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Primitive)]
#[repr(u8)]
pub enum PacketId {
OSU_USER_CHANGE_ACTION = 0,
OSU_SEND_PUBLIC_MESSAGE = 1,
OSU_USER_LOGOUT = 2,
OSU_USER_REQUEST_STATUS_UPDATE = 3,
OSU_PING = 4,
BANCHO_USER_LOGIN_REPLY = 5,
BANCHO_SEND_MESSAGE = 7,
BANCHO_PONG = 8,
BANCHO_HANDLE_IRC_CHANGE_USERNAME = 9,
BANCHO_HANDLE_IRC_QUIT = 10,
BANCHO_USER_STATS = 11,
BANCHO_USER_LOGOUT = 12,
BANCHO_SPECTATOR_JOINED = 13,
BANCHO_SPECTATOR_LEFT = 14,
BANCHO_SPECTATE_FRAMES = 15,
OSU_SPECTATE_START = 16,
OSU_SPECTATE_STOP = 17,
OSU_SPECTATE_FRAMES = 18,
BANCHO_VERSION_UPDATE = 19,
OSU_ERROR_REPORT = 20,
OSU_SPECTATE_CANT = 21,
BANCHO_SPECTATOR_CANT_SPECTATE = 22,
BANCHO_GET_ATTENTION = 23,
BANCHO_NOTIFICATION = 24,
OSU_SEND_PRIVATE_MESSAGE = 25,
BANCHO_UPDATE_MATCH = 26,
BANCHO_NEW_MATCH = 27,
BANCHO_DISBAND_MATCH = 28,
OSU_USER_PART_LOBBY = 29,
OSU_USER_JOIN_LOBBY = 30,
OSU_USER_CREATE_MATCH = 31,
OSU_USER_JOIN_MATCH = 32,
OSU_USER_PART_MATCH = 33,
BANCHO_TOGGLE_BLOCK_NON_FRIEND_DMS = 34,
BANCHO_MATCH_JOIN_SUCCESS = 36,
BANCHO_MATCH_JOIN_FAIL = 37,
OSU_MATCH_CHANGE_SLOT = 38,
OSU_USER_MATCH_READY = 39,
OSU_MATCH_LOCK = 40,
OSU_MATCH_CHANGE_SETTINGS = 41,
BANCHO_FELLOW_SPECTATOR_JOINED = 42,
BANCHO_FELLOW_SPECTATOR_LEFT = 43,
OSU_MATCH_START = 44,
BANCHO_ALL_PLAYERS_LOADED = 45,
BANCHO_MATCH_START = 46,
OSU_MATCH_SCORE_UPDATE = 47,
BANCHO_MATCH_SCORE_UPDATE = 48,
OSU_MATCH_COMPLETE = 49,
BANCHO_MATCH_TRANSFER_HOST = 50,
OSU_MATCH_CHANGE_MODS = 51,
OSU_MATCH_LOAD_COMPLETE = 52,
BANCHO_MATCH_ALL_PLAYERS_LOADED = 53,
OSU_MATCH_NO_BEATMAP = 54,
OSU_MATCH_NOT_READY = 55,
OSU_MATCH_FAILED = 56,
BANCHO_MATCH_PLAYER_FAILED = 57,
BANCHO_MATCH_COMPLETE = 58,
OSU_MATCH_HAS_BEATMAP = 59,
OSU_MATCH_SKIP_REQUEST = 60,
BANCHO_MATCH_SKIP = 61,
BANCHO_UNAUTHORIZED = 62,
OSU_USER_CHANNEL_JOIN = 63,
BANCHO_CHANNEL_JOIN_SUCCESS = 64,
BANCHO_CHANNEL_INFO = 65,
BANCHO_CHANNEL_KICK = 66,
BANCHO_CHANNEL_AUTO_JOIN = 67,
OSU_BEATMAP_INFO_REQUEST = 68,
BANCHO_BEATMAP_INFO_REPLY = 69,
OSU_MATCH_TRANSFER_HOST = 70,
BANCHO_PRIVILEGES = 71,
BANCHO_FRIENDS_LIST = 72,
OSU_USER_FRIEND_ADD = 73,
OSU_USER_FRIEND_REMOVE = 74,
BANCHO_PROTOCOL_VERSION = 75,
BANCHO_MAIN_MENU_ICON = 76,
OSU_MATCH_CHANGE_TEAM = 77,
OSU_USER_CHANNEL_PART = 78,
OSU_USER_RECEIVE_UPDATES = 79,
BANCHO_MONITOR = 80,
BANCHO_MATCH_PLAYER_SKIPPED = 81,
OSU_USER_SET_AWAY_MESSAGE = 82,
BANCHO_USER_PRESENCE = 83,
OSU_IRC_ONLY = 84,
OSU_USER_STATS_REQUEST = 85,
BANCHO_RESTART = 86,
OSU_MATCH_INVITE = 87,
BANCHO_MATCH_INVITE = 88,
BANCHO_CHANNEL_INFO_END = 89,
OSU_MATCH_CHANGE_PASSWORD = 90,
BANCHO_MATCH_CHANGE_PASSWORD = 91,
BANCHO_SILENCE_END = 92,
OSU_TOURNAMENT_MATCH_INFO_REQUEST = 93,
BANCHO_USER_SILENCED = 94,
BANCHO_USER_PRESENCE_SINGLE = 95,
BANCHO_USER_PRESENCE_BUNDLE = 96,
OSU_USER_PRESENCE_REQUEST = 97,
OSU_USER_PRESENCE_REQUEST_ALL = 98,
OSU_USER_TOGGLE_BLOCK_NON_FRIEND_DMS = 99,
BANCHO_USER_DM_BLOCKED = 100,
BANCHO_TARGET_IS_SILENCED = 101,
BANCHO_VERSION_UPDATE_FORCED = 102,
BANCHO_SWITCH_SERVER = 103,
BANCHO_ACCOUNT_RESTRICTED = 104,
BANCHO_RTX = 105,
BANCHO_MATCH_ABORT = 106,
BANCHO_SWITCH_TOURNAMENT_SERVER = 107,
OSU_TOURNAMENT_JOIN_MATCH_CHANNEL = 108,
OSU_TOURNAMENT_LEAVE_MATCH_CHANNEL = 109,
OSU_UNKNOWN_PACKET = 255,
}
impl std::fmt::Display for PacketId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}", self))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, ReadPacket, Default)]
pub struct BanchoMessage {
pub sender: String,
pub content: String,
pub target: String,
pub sender_id: i32,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PacketLength, Default)]
pub struct MatchData {
pub match_id: i32,
pub in_progress: bool,
pub match_type: i8,
pub play_mods: u32,
pub match_name: String,
#[length(self.password.as_ref().map(|pw| pw.packet_len()).unwrap_or(2))]
pub password: Option<String>,
pub beatmap_name: String,
pub beatmap_id: i32,
pub beatmap_md5: String,
pub slot_status: Vec<u8>,
pub slot_teams: Vec<u8>,
pub slot_players: Vec<i32>,
pub host_player_id: i32,
pub match_game_mode: u8,
pub win_condition: u8,
pub team_type: u8,
pub freemods: bool,
pub player_mods: Vec<i32>,
pub match_seed: i32,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PacketLength, Default)]
pub struct MatchUpdate {
pub data: MatchData,
pub send_password: bool,
}
impl Deref for MatchUpdate {
type Target = MatchData;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for MatchUpdate {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, ReadPacket, WritePacket, PacketLength, Default)]
pub struct ScoreFrame {
pub timestamp: i32,
pub id: u8,
pub n300: u16,
pub n100: u16,
pub n50: u16,
pub geki: u16,
pub katu: u16,
pub miss: u16,
pub score: i32,
pub combo: u16,
pub max_combo: u16,
pub perfect: bool,
pub hp: u8,
pub tag_byte: u8,
pub score_v2: bool,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, ReadPacket, WritePacket, PacketLength, Default)]
pub struct ClientChangeAction {
pub online_status: u8,
pub description: String,
pub beatmap_md5: String,
pub mods: u32,
pub mode: u8,
pub beatmap_id: i32,
}
#[derive(Debug, Clone)]
pub struct PayloadReader<'a> {
pub(crate) payload: &'a [u8],
pub(crate) index: usize,
}
impl<'a> PayloadReader<'a> {
#[inline]
pub fn new(payload: &'a [u8]) -> Self {
PayloadReader { payload, index: 0 }
}
#[inline]
pub fn read<T>(&mut self) -> Option<T>
where
T: BanchoPacketRead<T>,
{
T::read(self)
}
#[inline]
pub fn index(&self) -> usize {
self.index
}
#[inline]
pub fn payload(&self) -> &'a [u8] {
self.payload
}
#[inline]
pub fn reset(&mut self) {
self.index = 0
}
#[inline]
pub(crate) fn increase_index(&mut self, offset: usize) -> usize {
self.index += offset;
self.index
}
#[inline]
pub(crate) fn decrease_index(&mut self, offset: usize) -> usize {
self.index -= offset;
self.index
}
#[inline]
pub(crate) fn next_with_length(&mut self, length: usize) -> Option<&[u8]> {
self.index += length;
self.payload.get(self.index - length..self.index)
}
#[inline]
pub(crate) fn next_with_length_type<Len: Sized>(
&mut self,
) -> Option<&[u8]> {
self.next_with_length(std::mem::size_of::<Len>())
}
#[inline]
pub(crate) fn read_uleb128(&mut self) -> Option<u32> {
let (val, length) = uleb128_to_u32(self.payload.get(self.index..)?)?;
self.index += length;
Some(val)
}
}
#[derive(Debug, Clone)]
pub struct PacketReader<'a> {
buf: &'a [u8],
ptr: usize,
}
impl<'a> PacketReader<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> Self {
PacketReader { buf, ptr: 0 }
}
#[inline]
pub fn index(&self) -> usize {
self.ptr
}
#[inline]
pub fn buffer(&self) -> &'a [u8] {
self.buf
}
#[inline]
pub fn reset(&mut self) {
self.ptr = 0;
}
#[inline]
pub fn parse_header(header: &[u8]) -> Option<PacketHeader> {
let packet_id = *header.first()?;
Some(PacketHeader {
id: PacketId::from_u8(packet_id)
.unwrap_or(PacketId::OSU_UNKNOWN_PACKET),
payload_length: u32::from_le_bytes(
header[3..BANCHO_PACKET_HEADER_LENGTH].try_into().ok()?,
),
})
}
}
impl<'a> Iterator for PacketReader<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if (self.buf.len() - self.ptr) < BANCHO_PACKET_HEADER_LENGTH {
return None;
}
let header =
&self.buf[self.ptr..self.ptr + BANCHO_PACKET_HEADER_LENGTH];
self.ptr += BANCHO_PACKET_HEADER_LENGTH;
let PacketHeader { id, payload_length } =
PacketReader::parse_header(header)?;
let payload = if payload_length == 0 {
None
} else {
let next_payload_length = payload_length as usize;
self.ptr += next_payload_length;
self.buf.get(self.ptr - next_payload_length..self.ptr)
};
Some(Packet { id, payload })
}
}
#[derive(Debug, Clone)]
pub struct PacketBuilder {
buffer: Vec<u8>,
}
impl Default for PacketBuilder {
fn default() -> Self {
Self::new()
}
}
impl Deref for PacketBuilder {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl DerefMut for PacketBuilder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
}
}
impl PacketBuilder {
#[inline]
pub fn new() -> Self {
Self { buffer: Vec::new() }
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self { buffer: Vec::with_capacity(capacity) }
}
#[inline]
pub fn with_packet_id(packet_id: PacketId) -> Self {
Self { buffer: new_empty_packet(packet_id) }
}
#[inline]
pub fn build(self) -> Vec<u8> {
self.buffer
}
#[inline]
pub fn buffer(&self) -> &Vec<u8> {
&self.buffer
}
#[inline]
pub fn buffer_mut(&mut self) -> &mut Vec<u8> {
&mut self.buffer
}
#[inline]
pub fn from_batch<P, I>(packets: P) -> Self
where
I: IntoIterator<Item = u8>,
P: IntoIterator<Item = I>,
{
Self::new().add_batch(packets)
}
#[inline]
pub fn add_batch<P, I>(mut self, packets: P) -> Self
where
I: IntoIterator<Item = u8>,
P: IntoIterator<Item = I>,
{
self.add_batch_ref(packets);
self
}
#[inline]
pub fn add_batch_ref<P, I>(&mut self, packets: P) -> &Self
where
I: IntoIterator<Item = u8>,
P: IntoIterator<Item = I>,
{
for p in packets {
self.buffer.extend(p)
}
self
}
#[inline]
pub fn add<P>(mut self, packet: P) -> Self
where
P: IntoIterator<Item = u8>,
{
self.add_ref(packet);
self
}
#[inline]
pub fn add_ref<P>(&mut self, packet: P) -> &Self
where
P: IntoIterator<Item = u8>,
{
self.buffer.extend(packet);
self
}
}
impl From<Vec<u8>> for PacketBuilder {
#[inline]
fn from(buffer: Vec<u8>) -> Self {
Self { buffer }
}
}
pub trait BanchoPacketRead<T> {
fn read(reader: &mut PayloadReader) -> Option<T>;
}
impl BanchoPacketRead<String> for String {
#[inline]
fn read(reader: &mut PayloadReader) -> Option<String> {
if reader.payload.get(reader.index())? != &0xb {
return None;
}
reader.increase_index(1);
let data_length = reader.read_uleb128()? as usize;
let cur = reader.index;
reader.increase_index(data_length);
let data = reader.payload.get(cur..reader.index)?;
Some(std::str::from_utf8(data).ok()?.into())
}
}
impl BanchoPacketRead<bool> for bool {
#[inline]
fn read(reader: &mut PayloadReader) -> Option<bool> {
Some(reader.read::<i8>()? == 1)
}
}
macro_rules! impl_number {
($($t:ty),+) => {
$(impl BanchoPacketRead<$t> for $t {
#[inline]
fn read(reader: &mut PayloadReader) -> Option<$t> {
Some(<$t>::from_le_bytes(
reader.next_with_length_type::<$t>()?.try_into().ok()?,
))
}
})+
};
}
impl_number!(i8, u8, i16, u16, i32, u32, i64, u64);
macro_rules! impl_read_number_array {
($($t:ty),+) => {
$(impl BanchoPacketRead<Vec<$t>> for Vec<$t> {
#[inline]
fn read(reader: &mut PayloadReader) -> Option<Vec<$t>> {
let length_data = reader.next_with_length_type::<i16>()?;
let int_count = <i16>::from_le_bytes(length_data.try_into().ok()?) as usize;
let mut data = Vec::with_capacity(int_count);
for _ in 0..int_count {
data.push(<$t>::from_le_bytes(reader.next_with_length_type::<i32>()?.try_into().ok()?));
}
Some(data)
}
})+
};
}
impl_read_number_array!(i8, u8, i16, u16, i32, u32, i64, u64);
pub trait BanchoPacketWrite {
fn write_into_buf(self, buf: &mut Vec<u8>);
#[inline]
fn into_packet(self) -> Vec<u8>
where
Self: Sized,
{
let mut buf = Vec::new();
self.write_into_buf(&mut buf);
buf
}
}
impl BanchoPacketWrite for Cow<'_, str> {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
match self {
Cow::Borrowed(s) => s.write_into_buf(buf),
Cow::Owned(s) => s.write_into_buf(buf),
}
}
}
impl BanchoPacketWrite for &str {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
let byte_length = self.len();
if byte_length > 0 {
let (data, ptr) = u32_to_uleb128(byte_length as u32);
let length_uleb128 = &data[0..ptr];
let estimate_len = self.packet_len();
if buf.capacity() < estimate_len {
buf.reserve(estimate_len);
}
buf.push(0xb);
buf.extend(length_uleb128);
buf.extend(self.as_bytes());
} else {
buf.push(0);
}
}
}
impl BanchoPacketWrite for String {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
let byte_length = self.len();
if byte_length > 0 {
let (data, ptr) = u32_to_uleb128(byte_length as u32);
let length_uleb128 = &data[0..ptr];
let estimate_len = self.packet_len();
if buf.capacity() < estimate_len {
buf.reserve(estimate_len);
}
buf.push(0xb);
buf.extend(length_uleb128);
buf.extend(self.into_bytes());
} else {
buf.push(0);
}
}
}
impl BanchoPacketWrite for u8 {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
buf.push(self);
}
}
impl BanchoPacketWrite for &[u8] {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
buf.extend(self);
}
}
impl BanchoPacketWrite for Vec<u8> {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
buf.extend(self);
}
}
impl BanchoPacketWrite for bool {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
buf.push(if self { 1 } else { 0 });
}
}
macro_rules! impl_write_number {
($($t:ty),+) => {
$(
impl BanchoPacketWrite for $t {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
buf.extend(self.to_le_bytes())
}
}
impl BanchoPacketLength for $t {
#[inline]
fn packet_len(&self) -> usize {
std::mem::size_of::<$t>()
}
}
)+
}
}
impl_write_number!(i8, u16, i16, i32, u32, i64, u64, f32, f64);
macro_rules! impl_write_number_array {
($($t:ty),+) => {$(
impl BanchoPacketWrite for &[$t] { impl_write_number_array!(@bancho_packet_write_inner $t); }
impl BanchoPacketWrite for Vec<$t> { impl_write_number_array!(@bancho_packet_write_inner $t); }
impl BanchoPacketLength for &[$t] { impl_write_number_array!(@bancho_packet_length_inner $t); }
impl BanchoPacketLength for Vec<$t> { impl_write_number_array!(@bancho_packet_length_inner $t); }
)+};
(@bancho_packet_write_inner $t:ty) => {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
let estimate_len = self.packet_len();
if buf.capacity() < estimate_len {
buf.reserve(estimate_len);
}
buf.extend((self.len() as u16).to_le_bytes());
for int in self {
buf.extend(int.to_le_bytes())
}
}
};
(@bancho_packet_length_inner $t:ty) => {
#[inline]
fn packet_len(&self) -> usize {
std::mem::size_of::<u16>() + (std::mem::size_of::<$t>() * self.len())
}
}
}
impl_write_number_array!(i8, u16, i16, i32, u32, i64, u64, f32, f64);
impl BanchoPacketWrite for LoginResult {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
match self {
LoginResult::Success(user_id) => user_id,
LoginResult::Failed(reason) => reason as i32,
}
.write_into_buf(buf)
}
}
impl BanchoPacketWrite for MatchUpdate {
#[inline]
fn write_into_buf(mut self, buf: &mut Vec<u8>) {
let MatchUpdate {
data:
MatchData {
match_id,
in_progress,
match_type,
play_mods,
match_name,
password,
beatmap_name,
beatmap_id,
beatmap_md5,
slot_status,
slot_teams,
slot_players,
host_player_id,
match_game_mode,
win_condition,
team_type,
freemods,
player_mods,
match_seed,
},
send_password,
} = self;
let raw_password = password
.map(|password| {
if send_password {
let mut raw = Vec::with_capacity(password.packet_len());
password.write_into_buf(&mut raw);
raw
} else {
EMPTY_STRING_PACKET.to_vec()
}
})
.unwrap_or(b"\x00".to_vec());
buf.extend(data!(
match_id as u16,
in_progress,
match_type,
play_mods,
match_name,
raw_password,
beatmap_name,
beatmap_id,
beatmap_md5,
slot_status,
slot_teams,
slot_players,
host_player_id,
match_game_mode,
win_condition,
team_type,
freemods,
player_mods,
match_seed
));
}
}
impl BanchoPacketWrite for MatchData {
#[inline]
fn write_into_buf(self, buf: &mut Vec<u8>) {
MatchUpdate { data: self, send_password: true }.write_into_buf(buf);
}
}
pub trait BanchoPacketLength {
#[inline]
fn packet_len(&self) -> usize {
0
}
}
impl BanchoPacketLength for Cow<'_, str> {
#[inline]
fn packet_len(&self) -> usize {
match self {
Cow::Borrowed(s) => s.packet_len(),
Cow::Owned(s) => s.packet_len(),
}
}
}
impl BanchoPacketLength for &str {
#[inline]
fn packet_len(&self) -> usize {
if !self.is_empty() {
self.as_bytes().len() + 6
} else {
1
}
}
}
impl BanchoPacketLength for String {
#[inline]
fn packet_len(&self) -> usize {
if !self.is_empty() {
self.as_bytes().len() + 6
} else {
1
}
}
}
impl BanchoPacketLength for u8 {
#[inline]
fn packet_len(&self) -> usize {
std::mem::size_of::<u8>()
}
}
impl BanchoPacketLength for &[u8] {
#[inline]
fn packet_len(&self) -> usize {
self.len()
}
}
impl BanchoPacketLength for Vec<u8> {
#[inline]
fn packet_len(&self) -> usize {
self.len()
}
}
impl BanchoPacketLength for bool {
#[inline]
fn packet_len(&self) -> usize {
std::mem::size_of::<bool>()
}
}
impl BanchoPacketLength for LoginResult {
#[inline]
fn packet_len(&self) -> usize {
std::mem::size_of::<i32>()
}
}
impl<T> BanchoPacketLength for Option<T>
where
T: BanchoPacketLength,
{
#[inline]
fn packet_len(&self) -> usize {
self.as_ref().map(|t| t.packet_len()).unwrap_or(0)
}
}
#[inline]
pub fn u32_to_uleb128(mut unsigned: u32) -> ([u8; 5], usize) {
let mut data = [0, 0, 0, 0, 0];
let mut ptr = 0;
loop {
if unsigned < 0x80 {
break;
}
data[ptr] = ((unsigned & 0x7f) | 0x80) as u8;
ptr += 1;
unsigned >>= 7;
}
data[ptr] = unsigned as u8;
(data, ptr + 1)
}
#[inline]
pub fn uleb128_to_u32(uleb128_bytes: &[u8]) -> Option<(u32, usize)> {
let (mut val, mut shift, mut index) = (0, 0, 0);
loop {
let byte = uleb128_bytes.get(index)?;
index += 1;
if (byte & 0x80) == 0 {
val |= (*byte as u32) << shift;
return Some((val, index));
}
val |= ((byte & 0x7f) as u32) << shift;
shift += 7;
}
}
#[inline(always)]
pub fn new_empty_packet(packet_id: PacketId) -> Vec<u8> {
vec![packet_id as u8, 0, 0, 0, 0, 0, 0]
}
pub mod macros {
#[macro_export]
macro_rules! data {
($($item:expr$(,)*)*) => {
{
let mut estimate_capacity = 0;
$(estimate_capacity += $item.packet_len();)*
let mut buf = Vec::<u8>::with_capacity(estimate_capacity);
$($item.write_into_buf(&mut buf);)*
buf
}
};
(@capacity { $capacity:expr }, $($item:expr$(,)*)*) => {
{
let mut estimate_capacity = 0;
$(estimate_capacity += $item.packet_len();)*
let mut buf = Vec::<u8>::with_capacity($capacity + estimate_capacity);
$($item.write_into_buf(&mut buf);)*
buf
}
}
}
#[macro_export]
macro_rules! packet {
($packet_id:expr $(,$data:expr)*) => {
{
let mut estimate_capacity = 0;
$(estimate_capacity += $data.packet_len();)*
let mut packet = Vec::<u8>::with_capacity(estimate_capacity);
packet.extend(&[$packet_id as u8, 0, 0, 0, 0, 0, 0]);
$($data.write_into_buf(&mut packet);)*
let packet_length_bytes =
((packet.len() - $crate::BANCHO_PACKET_HEADER_LENGTH) as i32)
.to_le_bytes();
packet[3] = packet_length_bytes[0];
packet[4] = packet_length_bytes[1];
packet[5] = packet_length_bytes[2];
packet[6] = packet_length_bytes[3];
packet
}
}
}
#[macro_export]
macro_rules! packet_struct {
(
$packet_id: expr,
$(#[$struct_meta:meta])*
$struct_name:ident $(< $($lifetimes:lifetime),* >)? {
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_type:ty$(,)*
)*
},
fn $fn:ident ($self:ident) -> $ret:ty $body:block
) => {
$(#[$struct_meta])*
#[derive(Debug, Clone, Default)]
pub struct $struct_name $(< $($lifetimes),* >)? {
$(
$(#[$field_meta])*
pub $field_name: $field_type,
)*
}
impl $(< $($lifetimes),* >)? $crate::BanchoPacketLength for $struct_name $(< $($lifetimes),* >)? {
#[inline]
fn packet_len(&self) -> usize {
let mut _len = 0;
$(_len += self.$field_name.packet_len();)*
_len
}
}
impl$(< $($lifetimes),* >)? $struct_name$(< $($lifetimes),* >)? {
#[inline]
pub fn new(
$($field_name: $field_type,)*
) -> Self {
Self { $($field_name,)* }
}
#[inline]
pub fn pack(
$($field_name: $field_type,)*
) -> Vec<u8> {
$crate::BanchoPacket::into_packet_data(Self { $($field_name,)* })
}
}
impl$(< $($lifetimes),* >)? $crate::BanchoPacket for $struct_name$(< $($lifetimes),* >)? {
const ID: $crate::PacketId = $packet_id;
#[inline]
fn $fn ($self) -> $ret $body
}
impl$(< $($lifetimes),* >)? IntoIterator for $struct_name$(< $($lifetimes),* >)? {
type Item = u8;
type IntoIter = std::vec::IntoIter<u8>;
fn into_iter(self) -> Self::IntoIter {
$crate::BanchoPacket::into_packet_data(self).into_iter()
}
}
impl$(< $($lifetimes),* >)? From<$struct_name$(< $($lifetimes),* >)?> for Vec<u8> {
fn from(packet: $struct_name$(< $($lifetimes),* >)?) -> Vec<u8> {
$crate::BanchoPacket::into_packet_data(packet)
}
}
};
(
$packet_id: expr,
$(#[$struct_meta:meta])*
$struct_name:ident $(< $($lifetimes:lifetime),* >)? {
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_type:ty$(,)*
)*
}
) => {
packet_struct!(
$packet_id,
$(#[$struct_meta])*
$struct_name $(< $($lifetimes),* >)? {
$(
$(#[$field_meta])*
$field_name: $field_type,
)*
},
fn into_packet_data(self) -> Vec<u8> {
$crate::packet!(
Self::ID
$(,self.$field_name)*
)
}
);
}
}
}