From 6e818852d868eea963dd2b8489ba75b65c56fb1c Mon Sep 17 00:00:00 2001 From: EightFactorial Date: Mon, 30 Jan 2023 16:18:14 -0800 Subject: [PATCH] More packet fixes, tests, handle error (#61) * Fix packet, fix tests, fixedbitsets * Clippy: Nightmare Mode * Fix mistake * simplify impl Display and make thing pub --------- Co-authored-by: mat --- azalea-auth/src/sessionserver.rs | 3 + azalea-protocol/examples/handshake_proxy.rs | 2 +- azalea-protocol/src/connect.rs | 8 + azalea-protocol/src/lib.rs | 20 ++- .../game/clientbound_boss_event_packet.rs | 19 +- .../game/clientbound_commands_packet.rs | 166 ++++++++++++------ .../game/clientbound_explode_packet.rs | 6 +- .../game/clientbound_map_item_data_packet.rs | 94 ++++------ .../clientbound_player_abilities_packet.rs | 23 +-- .../game/clientbound_player_chat_packet.rs | 11 +- .../clientbound_player_position_packet.rs | 27 +-- ...lientbound_section_blocks_update_packet.rs | 2 +- .../game/clientbound_set_equipment_packet.rs | 1 + .../game/clientbound_set_objective_packet.rs | 2 +- .../clientbound_set_player_team_packet.rs | 2 +- .../game/clientbound_set_score_packet.rs | 2 +- .../game/clientbound_stop_sound_packet.rs | 16 +- .../clientbound_update_advancements_packet.rs | 132 ++++++++------ .../serverbound_client_information_packet.rs | 133 +++++++++++++- .../game/serverbound_interact_packet.rs | 6 +- .../serverbound_player_abilities_packet.rs | 14 +- .../game/serverbound_player_input_packet.rs | 18 +- .../serverbound_set_command_block_packet.rs | 24 ++- .../serverbound_set_structure_block_packet.rs | 20 +-- .../game/serverbound_use_item_on_packet.rs | 12 +- azalea-protocol/src/packets/mod.rs | 3 +- azalea-protocol/src/read.rs | 65 +++---- azalea-protocol/src/resolver.rs | 1 + 28 files changed, 499 insertions(+), 333 deletions(-) diff --git a/azalea-auth/src/sessionserver.rs b/azalea-auth/src/sessionserver.rs index 502ae098..5eefb292 100755 --- a/azalea-auth/src/sessionserver.rs +++ b/azalea-auth/src/sessionserver.rs @@ -24,6 +24,8 @@ pub enum ClientSessionServerError { Unknown(String), #[error("Forbidden operation (expired session?)")] ForbiddenOperation, + #[error("RateLimiter disallowed request")] + RateLimited, #[error("Unexpected response from sessionserver (status code {status_code}): {body}")] UnexpectedResponse { status_code: u16, body: String }, } @@ -95,6 +97,7 @@ pub async fn join( _ => Err(ClientSessionServerError::Unknown(forbidden.error)), } } + StatusCode::TOO_MANY_REQUESTS => Err(ClientSessionServerError::RateLimited), status_code => { // log the headers debug!("Error headers: {:#?}", res.headers()); diff --git a/azalea-protocol/examples/handshake_proxy.rs b/azalea-protocol/examples/handshake_proxy.rs index 895bdae7..6bcb84a6 100644 --- a/azalea-protocol/examples/handshake_proxy.rs +++ b/azalea-protocol/examples/handshake_proxy.rs @@ -150,7 +150,7 @@ async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> { if let Some(id) = hello.profile_id { id.to_string() } else { - "".to_string() + String::new() } ); diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index 06b306b9..cb837ba5 100755 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -189,6 +189,7 @@ where } /// Split the reader and writer into two objects. This doesn't allocate. + #[must_use] pub fn into_split(self) -> (ReadConnection, WriteConnection) { (self.reader, self.writer) } @@ -229,12 +230,14 @@ impl Connection { /// Change our state from handshake to login. This is the state that is used /// for logging in. + #[must_use] pub fn login(self) -> Connection { Connection::from(self) } /// Change our state from handshake to status. This is the state that is /// used for pinging the server. + #[must_use] pub fn status(self) -> Connection { Connection::from(self) } @@ -265,6 +268,7 @@ impl Connection { /// Change our state from login to game. This is the state that's used when /// you're actually in the game. + #[must_use] pub fn game(self) -> Connection { Connection::from(self) } @@ -343,12 +347,14 @@ impl Connection { impl Connection { /// Change our state from handshake to login. This is the state that is used /// for logging in. + #[must_use] pub fn login(self) -> Connection { Connection::from(self) } /// Change our state from handshake to status. This is the state that is /// used for pinging the server. + #[must_use] pub fn status(self) -> Connection { Connection::from(self) } @@ -379,6 +385,7 @@ impl Connection { /// Change our state from login to game. This is the state that's used when /// the client is actually in the game. + #[must_use] pub fn game(self) -> Connection { Connection::from(self) } @@ -406,6 +413,7 @@ where { /// Creates a `Connection` of a type from a `Connection` of another type. /// Useful for servers or custom packets. + #[must_use] pub fn from(connection: Connection) -> Connection where R2: ProtocolPacket + Debug, diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index 3f838d2a..7c21f493 100644 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -1,7 +1,7 @@ //! A low-level crate to send and receive Minecraft packets. //! //! You should probably use [`azalea`] or [`azalea_client`] instead, as -//! azalea_protocol delegates much of the work, such as auth, to the user of +//! `azalea_protocol` delegates much of the work, such as auth, to the user of //! the crate. //! //! [`azalea`]: https://crates.io/crates/azalea @@ -13,7 +13,7 @@ #![feature(error_generic_member_access)] #![feature(provide_any)] -use std::{net::SocketAddr, str::FromStr}; +use std::{fmt::Display, net::SocketAddr, str::FromStr}; #[cfg(feature = "connecting")] pub mod connect; @@ -27,7 +27,7 @@ pub mod write; /// /// # Examples /// -/// ServerAddress implements TryFrom<&str>, so you can use it like this: +/// `ServerAddress` implements TryFrom<&str>, so you can use it like this: /// ``` /// use azalea_protocol::ServerAddress; /// @@ -45,7 +45,7 @@ impl<'a> TryFrom<&'a str> for ServerAddress { type Error = String; /// Convert a Minecraft server address (host:port, the port is optional) to - /// a ServerAddress + /// a `ServerAddress` fn try_from(string: &str) -> Result { if string.is_empty() { return Err("Empty string".to_string()); @@ -60,9 +60,9 @@ impl<'a> TryFrom<&'a str> for ServerAddress { } impl From for ServerAddress { - /// Convert an existing SocketAddr into a ServerAddress. This just converts - /// the ip to a string and passes along the port. The resolver will realize - /// it's already an IP address and not do any DNS requests. + /// Convert an existing `SocketAddr` into a `ServerAddress`. This just + /// converts the ip to a string and passes along the port. The resolver + /// will realize it's already an IP address and not do any DNS requests. fn from(addr: SocketAddr) -> Self { ServerAddress { host: addr.ip().to_string(), @@ -71,6 +71,12 @@ impl From for ServerAddress { } } +impl Display for ServerAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.host, self.port) + } +} + #[cfg(test)] mod tests { use std::io::Cursor; diff --git a/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs b/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs index 59378d3e..3ffbaea1 100755 --- a/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs @@ -2,6 +2,7 @@ use azalea_buf::{ BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable, }; use azalea_chat::Component; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ClientboundGamePacket; use std::io::Cursor; use std::io::Write; @@ -116,28 +117,28 @@ pub struct Properties { impl McBufReadable for Properties { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; + let set = FixedBitSet::<3>::read_from(buf)?; Ok(Self { - darken_screen: byte & 1 != 0, - play_music: byte & 2 != 0, - create_world_fog: byte & 4 != 0, + darken_screen: set.index(0), + play_music: set.index(1), + create_world_fog: set.index(2), }) } } impl McBufWritable for Properties { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut byte = 0; + let mut set = FixedBitSet::<3>::new(); if self.darken_screen { - byte |= 1; + set.set(0); } if self.play_music { - byte |= 2; + set.set(1); } if self.create_world_fog { - byte |= 4; + set.set(2); } - u8::write_into(&byte, buf)?; + set.write_into(buf)?; Ok(()) } } diff --git a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs index 39e807cf..fa11a355 100755 --- a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs @@ -2,6 +2,7 @@ use azalea_buf::BufReadError; use azalea_buf::McBuf; use azalea_buf::McBufVarReadable; use azalea_buf::{McBufReadable, McBufVarWritable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_core::ResourceLocation; use azalea_protocol_macros::ClientboundGamePacket; use log::warn; @@ -15,7 +16,7 @@ pub struct ClientboundCommandsPacket { pub root_index: u32, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct BrigadierNodeStub { pub is_executable: bool, pub children: Vec, @@ -23,7 +24,7 @@ pub struct BrigadierNodeStub { pub node_type: NodeType, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq)] pub struct BrigadierNumber { pub min: Option, pub max: Option, @@ -33,15 +34,29 @@ impl BrigadierNumber { BrigadierNumber { min, max } } } +impl PartialEq for BrigadierNumber { + fn eq(&self, other: &Self) -> bool { + match (&self.min, &self.max, &other.min, &other.max) { + (Some(f_min), None, Some(s_min), None) => f_min == s_min, + (None, Some(f_max), None, Some(s_max)) => f_max == s_max, + (Some(f_min), Some(f_max), Some(s_min), Some(s_max)) => { + f_min == s_min && f_max == s_max + } + (None, None, None, None) => true, + _ => false, + } + } +} + impl McBufReadable for BrigadierNumber { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let flags = u8::read_from(buf)?; - let min = if flags & 0x01 != 0 { + let flags = FixedBitSet::<2>::read_from(buf)?; + let min = if flags.index(0) { Some(T::read_from(buf)?) } else { None }; - let max = if flags & 0x02 != 0 { + let max = if flags.index(1) { Some(T::read_from(buf)?) } else { None @@ -51,12 +66,12 @@ impl McBufReadable for BrigadierNumber { } impl McBufWritable for BrigadierNumber { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut flags: u8 = 0; + let mut flags = FixedBitSet::<2>::new(); if self.min.is_some() { - flags |= 0x01; + flags.set(0); } if self.max.is_some() { - flags |= 0x02; + flags.set(1); } flags.write_into(buf)?; if let Some(min) = &self.min { @@ -69,7 +84,7 @@ impl McBufWritable for BrigadierNumber { } } -#[derive(Debug, Clone, Copy, McBuf)] +#[derive(Debug, Clone, Copy, McBuf, PartialEq, Eq)] pub enum BrigadierString { /// Reads a single word SingleWord = 0, @@ -80,7 +95,7 @@ pub enum BrigadierString { GreedyPhrase = 2, } -#[derive(Debug, Clone, McBuf)] +#[derive(Debug, Clone, McBuf, PartialEq)] pub enum BrigadierParser { Bool, Float(BrigadierNumber), @@ -132,28 +147,28 @@ pub enum BrigadierParser { Uuid, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct EntityParser { pub single: bool, pub players_only: bool, } impl McBufReadable for EntityParser { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let flags = u8::read_from(buf)?; + let flags = FixedBitSet::<2>::read_from(buf)?; Ok(EntityParser { - single: flags & 0x01 != 0, - players_only: flags & 0x02 != 0, + single: flags.index(0), + players_only: flags.index(1), }) } } impl McBufWritable for EntityParser { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut flags: u8 = 0; + let mut flags = FixedBitSet::<2>::new(); if self.single { - flags |= 0x01; + flags.set(0); } if self.players_only { - flags |= 0x02; + flags.set(1); } flags.write_into(buf)?; Ok(()) @@ -163,17 +178,15 @@ impl McBufWritable for EntityParser { // TODO: BrigadierNodeStub should have more stuff impl McBufReadable for BrigadierNodeStub { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let flags = u8::read_from(buf)?; - if flags > 31 { - warn!( - "Warning: The flags from a Brigadier node are over 31 ({flags}; {flags:#b}). This is probably a bug.", - ); + let flags = FixedBitSet::<8>::read_from(buf)?; + if flags.index(5) || flags.index(6) || flags.index(7) { + warn!("Warning: The flags from a Brigadier node are over 31. This is probably a bug.",); } - let node_type = flags & 0x03; - let is_executable = flags & 0x04 != 0; - let has_redirect = flags & 0x08 != 0; - let has_suggestions_type = flags & 0x10 != 0; + let node_type = u8::from(flags.index(0)) + (u8::from(flags.index(1)) * 2); + let is_executable = flags.index(2); + let has_redirect = flags.index(3); + let has_suggestions_type = flags.index(4); let children = Vec::::var_read_from(buf)?; let redirect_node = if has_redirect { @@ -224,16 +237,17 @@ impl McBufReadable for BrigadierNodeStub { impl McBufWritable for BrigadierNodeStub { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let mut flags = FixedBitSet::<4>::new(); + if self.is_executable { + flags.set(2); + } + if self.redirect_node.is_some() { + flags.set(3); + } + match &self.node_type { NodeType::Root => { - let mut flags = 0x00; - if self.is_executable { - flags |= 0x04; - } - if self.redirect_node.is_some() { - flags |= 0x08; - } - flags.var_write_into(buf)?; + flags.write_into(buf)?; self.children.var_write_into(buf)?; @@ -242,14 +256,8 @@ impl McBufWritable for BrigadierNodeStub { } } NodeType::Literal { name } => { - let mut flags = 0x01; - if self.is_executable { - flags |= 0x04; - } - if self.redirect_node.is_some() { - flags |= 0x08; - } - flags.var_write_into(buf)?; + flags.set(0); + flags.write_into(buf)?; self.children.var_write_into(buf)?; @@ -264,17 +272,11 @@ impl McBufWritable for BrigadierNodeStub { parser, suggestions_type, } => { - let mut flags = 0x02; - if self.is_executable { - flags |= 0x04; - } - if self.redirect_node.is_some() { - flags |= 0x08; - } + flags.set(1); if suggestions_type.is_some() { - flags |= 0x10; + flags.set(4); } - flags.var_write_into(buf)?; + flags.write_into(buf)?; self.children.var_write_into(buf)?; @@ -294,7 +296,7 @@ impl McBufWritable for BrigadierNodeStub { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum NodeType { Root, Literal { @@ -308,11 +310,67 @@ pub enum NodeType { } impl BrigadierNodeStub { + #[must_use] pub fn name(&self) -> Option<&str> { match &self.node_type { NodeType::Root => None, - NodeType::Literal { name } => Some(name), - NodeType::Argument { name, .. } => Some(name), + NodeType::Literal { name } | NodeType::Argument { name, .. } => Some(name), } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_brigadier_node_stub_root() { + let data = BrigadierNodeStub { + is_executable: false, + children: vec![1, 2], + redirect_node: None, + node_type: NodeType::Root, + }; + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap(); + assert_eq!(data, read_data); + } + + #[test] + fn test_brigadier_node_stub_literal() { + let data = BrigadierNodeStub { + is_executable: true, + children: vec![], + redirect_node: None, + node_type: NodeType::Literal { + name: "String".to_string(), + }, + }; + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap(); + assert_eq!(data, read_data); + } + + #[test] + fn test_brigadier_node_stub_argument() { + let data = BrigadierNodeStub { + is_executable: false, + children: vec![6, 9], + redirect_node: Some(5), + node_type: NodeType::Argument { + name: "position".to_string(), + parser: BrigadierParser::Vec3, + suggestions_type: Some(ResourceLocation::new("minecraft:test_suggestion").unwrap()), + }, + }; + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + let read_data = BrigadierNodeStub::read_from(&mut data_cursor).unwrap(); + assert_eq!(data, read_data); + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_explode_packet.rs b/azalea-protocol/src/packets/game/clientbound_explode_packet.rs index 58070769..2146a254 100755 --- a/azalea-protocol/src/packets/game/clientbound_explode_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_explode_packet.rs @@ -31,9 +31,9 @@ impl McBufReadable for ClientboundExplodePacket { let mut to_blow = Vec::with_capacity(to_blow_len as usize); for _ in 0..to_blow_len { // the bytes are offsets from the main x y z - let x = x_floor + i8::read_from(buf)? as i32; - let y = y_floor + i8::read_from(buf)? as i32; - let z = z_floor + i8::read_from(buf)? as i32; + let x = x_floor + i32::from(i8::read_from(buf)?); + let y = y_floor + i32::from(i8::read_from(buf)?); + let z = z_floor + i32::from(i8::read_from(buf)?); to_blow.push(BlockPos { x, y, z }); } diff --git a/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs index 94593e3e..38d92135 100755 --- a/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs @@ -1,71 +1,15 @@ -use azalea_buf::{BufReadError, McBuf}; -use azalea_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; use azalea_chat::Component; use azalea_protocol_macros::ClientboundGamePacket; -use std::io::{Cursor, Write}; -#[derive(Clone, Debug, ClientboundGamePacket)] +#[derive(Clone, Debug, ClientboundGamePacket, McBuf)] pub struct ClientboundMapItemDataPacket { - // #[var] + #[var] pub map_id: u32, pub scale: u8, pub locked: bool, - pub decorations: Vec, - pub color_patch: Option, -} - -impl McBufReadable for ClientboundMapItemDataPacket { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let map_id = u32::var_read_from(buf)?; - let scale = u8::read_from(buf)?; - let locked = bool::read_from(buf)?; - let decorations = Option::>::read_from(buf)?.unwrap_or_default(); - - let width = u8::read_from(buf)?; - let color_patch = if width == 0 { - None - } else { - let height = u8::read_from(buf)?; - let start_x = u8::read_from(buf)?; - let start_y = u8::read_from(buf)?; - let map_colors = Vec::::read_from(buf)?; - Some(MapPatch { - width, - height, - start_x, - start_y, - map_colors, - }) - }; - - Ok(Self { - map_id, - scale, - locked, - decorations, - color_patch, - }) - } -} - -impl McBufWritable for ClientboundMapItemDataPacket { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.map_id.var_write_into(buf)?; - self.scale.write_into(buf)?; - self.locked.write_into(buf)?; - (!self.decorations.is_empty()).write_into(buf)?; - self.decorations.write_into(buf)?; - if let Some(color_patch) = &self.color_patch { - color_patch.width.write_into(buf)?; - color_patch.height.write_into(buf)?; - color_patch.start_x.write_into(buf)?; - color_patch.start_y.write_into(buf)?; - color_patch.map_colors.write_into(buf)?; - } else { - 0u8.write_into(buf)?; - } - Ok(()) - } + pub decorations: Option>, + pub color_patch: OptionalMapPatch, } #[derive(Clone, Debug, McBuf)] @@ -80,11 +24,35 @@ pub struct MapDecoration { } #[derive(Debug, Clone)] +pub struct OptionalMapPatch(pub Option); + +impl McBufReadable for OptionalMapPatch { + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result { + let pos = buf.position(); + Ok(Self(if u8::read_from(buf)? == 0 { + None + } else { + buf.set_position(pos); + Some(MapPatch::read_from(buf)?) + })) + } +} + +impl McBufWritable for OptionalMapPatch { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + match &self.0 { + None => 0u8.write_into(buf), + Some(m) => m.write_into(buf), + } + } +} + +#[derive(Debug, Clone, McBuf)] pub struct MapPatch { - pub start_x: u8, - pub start_y: u8, pub width: u8, pub height: u8, + pub start_x: u8, + pub start_y: u8, pub map_colors: Vec, } diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs index 192a7bf4..12fd45e8 100755 --- a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs @@ -1,5 +1,6 @@ use azalea_buf::{BufReadError, McBuf}; use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ClientboundGamePacket; use std::io::{Cursor, Write}; @@ -21,31 +22,31 @@ pub struct PlayerAbilitiesFlags { impl McBufReadable for PlayerAbilitiesFlags { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; + let set = FixedBitSet::<4>::read_from(buf)?; Ok(PlayerAbilitiesFlags { - invulnerable: byte & 1 != 0, - flying: byte & 2 != 0, - can_fly: byte & 4 != 0, - instant_break: byte & 8 != 0, + invulnerable: set.index(0), + flying: set.index(1), + can_fly: set.index(2), + instant_break: set.index(3), }) } } impl McBufWritable for PlayerAbilitiesFlags { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut byte = 0; + let mut set = FixedBitSet::<4>::new(); if self.invulnerable { - byte |= 0b1; + set.set(0); } if self.flying { - byte |= 0b10; + set.set(1); } if self.can_fly { - byte |= 0b100; + set.set(2); } if self.instant_break { - byte |= 0b1000; + set.set(3); } - u8::write_into(&byte, buf) + set.write_into(buf) } } diff --git a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs index a735c907..098ffa03 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs @@ -90,6 +90,7 @@ impl ClientboundPlayerChatPacket { /// Returns the content of the message. If you want to get the Component /// for the whole message including the sender part, use /// [`ClientboundPlayerChatPacket::message`]. + #[must_use] pub fn content(&self) -> Component { self.unsigned_content .clone() @@ -97,6 +98,7 @@ impl ClientboundPlayerChatPacket { } /// Get the full message, including the sender part. + #[must_use] pub fn message(&self) -> Component { let sender = self.chat_type.name.clone(); let content = self.content(); @@ -119,6 +121,7 @@ impl ClientboundPlayerChatPacket { } impl ChatType { + #[must_use] pub fn chat_translation_key(&self) -> &'static str { match self { ChatType::Chat => "chat.type.text", @@ -131,15 +134,11 @@ impl ChatType { } } + #[must_use] pub fn narrator_translation_key(&self) -> &'static str { match self { - ChatType::Chat => "chat.type.text.narrate", - ChatType::SayCommand => "chat.type.text.narrate", - ChatType::MsgCommandIncoming => "chat.type.text.narrate", - ChatType::MsgCommandOutgoing => "chat.type.text.narrate", - ChatType::TeamMsgCommandIncoming => "chat.type.text.narrate", - ChatType::TeamMsgCommandOutgoing => "chat.type.text.narrate", ChatType::EmoteCommand => "chat.type.emote", + _ => "chat.type.text.narrate", } } } diff --git a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs index 03a2658e..3ad422e7 100755 --- a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs @@ -1,5 +1,6 @@ use azalea_buf::{BufReadError, McBuf}; use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ClientboundGamePacket; use std::io::{Cursor, Write}; @@ -29,35 +30,35 @@ pub struct RelativeArguments { impl McBufReadable for RelativeArguments { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; + let set = FixedBitSet::<5>::read_from(buf)?; Ok(RelativeArguments { - x: byte & 0b1 != 0, - y: byte & 0b10 != 0, - z: byte & 0b100 != 0, - y_rot: byte & 0b1000 != 0, - x_rot: byte & 0b10000 != 0, + x: set.index(0), + y: set.index(1), + z: set.index(2), + y_rot: set.index(3), + x_rot: set.index(4), }) } } impl McBufWritable for RelativeArguments { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut byte = 0; + let mut set = FixedBitSet::<5>::new(); if self.x { - byte |= 0b1; + set.set(0); } if self.y { - byte |= 0b10; + set.set(1); } if self.z { - byte |= 0b100; + set.set(2); } if self.y_rot { - byte |= 0b1000; + set.set(3); } if self.x_rot { - byte |= 0b10000; + set.set(4); } - u8::write_into(&byte, buf) + set.write_into(buf) } } diff --git a/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs b/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs index f9b1cd1e..1a8e0d40 100755 --- a/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs @@ -38,7 +38,7 @@ impl McBufReadable for BlockStateWithPosition { impl McBufWritable for BlockStateWithPosition { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { let data = (self.state as u64) << 12 - | ((self.pos.x as u64) << 8 | (self.pos.z as u64) << 4 | (self.pos.y as u64)); + | (u64::from(self.pos.x) << 8 | u64::from(self.pos.z) << 4 | u64::from(self.pos.y)); u64::var_write_into(&data, buf)?; Ok(()) } diff --git a/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs index 315f1590..11472591 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs @@ -65,6 +65,7 @@ pub enum EquipmentSlot { } impl EquipmentSlot { + #[must_use] pub fn from_byte(byte: u8) -> Option { match byte { 0 => Some(EquipmentSlot::MainHand), diff --git a/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs index ad75a1c3..9e2da325 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs @@ -22,7 +22,7 @@ impl McBufReadable for Method { 0 => Method::Add(DisplayInfo::read_from(buf)?), 1 => Method::Remove, 2 => Method::Change(DisplayInfo::read_from(buf)?), - id => return Err(BufReadError::UnexpectedEnumVariant { id: id as i32 }), + id => return Err(BufReadError::UnexpectedEnumVariant { id: i32::from(id) }), }) } } diff --git a/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs index 2550a98c..0b3c1e24 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs @@ -26,7 +26,7 @@ impl McBufReadable for Method { 2 => Method::Change(Parameters::read_from(buf)?), 3 => Method::Join(PlayerList::read_from(buf)?), 4 => Method::Leave(PlayerList::read_from(buf)?), - id => return Err(BufReadError::UnexpectedEnumVariant { id: id as i32 }), + id => return Err(BufReadError::UnexpectedEnumVariant { id: i32::from(id) }), }) } } diff --git a/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs index 7d055a14..56d14b94 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs @@ -45,7 +45,7 @@ impl McBufWritable for ClientboundSetScorePacket { // convert None to an empty string self.objective_name .as_ref() - .unwrap_or(&"".to_string()) + .unwrap_or(&String::new()) .write_into(buf)?; if let Method::Change { score } = self.method { score.var_write_into(buf)?; diff --git a/azalea-protocol/src/packets/game/clientbound_stop_sound_packet.rs b/azalea-protocol/src/packets/game/clientbound_stop_sound_packet.rs index e0e5db05..5ea80e67 100755 --- a/azalea-protocol/src/packets/game/clientbound_stop_sound_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_stop_sound_packet.rs @@ -1,5 +1,5 @@ use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; -use azalea_core::ResourceLocation; +use azalea_core::{FixedBitSet, ResourceLocation}; use azalea_protocol_macros::ClientboundGamePacket; use std::io::{Cursor, Write}; @@ -13,13 +13,13 @@ pub struct ClientboundStopSoundPacket { impl McBufReadable for ClientboundStopSoundPacket { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; - let source = if byte & 1 != 0 { + let set = FixedBitSet::<2>::read_from(buf)?; + let source = if set.index(0) { Some(SoundSource::read_from(buf)?) } else { None }; - let name = if byte & 2 != 0 { + let name = if set.index(1) { Some(ResourceLocation::read_from(buf)?) } else { None @@ -31,14 +31,14 @@ impl McBufReadable for ClientboundStopSoundPacket { impl McBufWritable for ClientboundStopSoundPacket { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut byte = 0u8; + let mut set = FixedBitSet::<2>::new(); if self.source.is_some() { - byte |= 1; + set.set(0); } if self.name.is_some() { - byte |= 2; + set.set(1); } - byte.write_into(buf)?; + set.write_into(buf)?; if let Some(source) = &self.source { source.write_into(buf)?; } diff --git a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs index b18193d0..e2a46d38 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs @@ -17,10 +17,8 @@ pub struct ClientboundUpdateAdvancementsPacket { pub struct Advancement { parent_id: Option, display: Option, - // rewards: AdvancementRewards.EMPTY, criteria: HashMap, requirements: Vec>, - // requirements_strategy: RequirementsStrategy.AND } #[derive(Clone, Debug)] @@ -87,11 +85,11 @@ impl azalea_buf::McBufReadable for DisplayInfo { description, icon, frame, + show_toast, + hidden, background, x, y, - hidden, - show_toast, }) } } @@ -114,57 +112,77 @@ pub struct CriterionProgress { date: Option, } -// #[cfg(test)] -// mod tests { -// use super::*; -// use azalea_buf::{McBufReadable, McBufWritable}; -// use azalea_core::ResourceLocation; -// use azalea_protocol_macros::ClientboundGamePacket; -// use std::io::Cursor; +#[cfg(test)] +mod tests { + use super::*; + use azalea_buf::{McBufReadable, McBufWritable}; + use azalea_core::ResourceLocation; + use std::io::Cursor; -// #[test] -// fn test() { -// let mut buf = Cursor::new(Vec::new()); -// let packet = ClientboundUpdateAdvancementsPacket { -// reset: true, -// added: [( -// ResourceLocation::new("minecraft:test").unwrap(), -// Advancement { -// parent_id: None, -// display: Some(DisplayInfo { -// title: Component::from("title".to_string()), -// description: -// Component::from("description".to_string()), icon: -// Slot::Empty, frame: FrameType::Task, -// show_toast: true, -// hidden: false, -// background: None, -// x: 0.0, -// y: 0.0, -// }), -// criteria: HashMap::new(), -// requirements: Vec::new(), -// }, -// )] -// .into_iter() -// .collect(), -// removed: vec![ResourceLocation::new("minecraft:test2").unwrap()], -// progress: [( -// ResourceLocation::new("minecraft:test3").unwrap(), -// [( -// ResourceLocation::new("minecraft:test4").unwrap(), -// CriterionProgress { -// date: Some(123456789), -// }, -// )] -// .into_iter() -// .collect(), -// )] -// .into_iter() -// .collect(), -// }; -// packet.write_into(&mut buf).unwrap(); -// let packet = ClientboundUpdateAdvancementsPacket::read_from(&mut -// buf).unwrap(); assert_eq!(packet.reset, true); -// } -// } + #[test] + fn test() { + let packet = ClientboundUpdateAdvancementsPacket { + reset: true, + added: [( + ResourceLocation::new("minecraft:test").unwrap(), + Advancement { + parent_id: None, + display: Some(DisplayInfo { + title: Component::from("title".to_string()), + description: Component::from("description".to_string()), + icon: Slot::Empty, + frame: FrameType::Task, + show_toast: true, + hidden: false, + background: None, + x: 0.0, + y: 0.0, + }), + criteria: HashMap::new(), + requirements: Vec::new(), + }, + )] + .into_iter() + .collect(), + removed: vec![ResourceLocation::new("minecraft:test2").unwrap()], + progress: [( + ResourceLocation::new("minecraft:test3").unwrap(), + [( + ResourceLocation::new("minecraft:test4").unwrap(), + CriterionProgress { + date: Some(123456789), + }, + )] + .into_iter() + .collect(), + )] + .into_iter() + .collect(), + }; + + let mut data = Vec::new(); + packet.write_into(&mut data).unwrap(); + let mut buf: Cursor<&[u8]> = Cursor::new(&data); + + let read_packet = ClientboundUpdateAdvancementsPacket::read_from(&mut buf).unwrap(); + assert_eq!(packet.reset, read_packet.reset); + assert_eq!(packet.removed, read_packet.removed); + + let advancement = packet + .added + .get(&ResourceLocation::new("minecraft:test").unwrap()) + .unwrap() + .clone(); + let read_advancement = read_packet + .added + .get(&ResourceLocation::new("minecraft:test").unwrap()) + .unwrap() + .clone(); + assert_eq!(advancement.parent_id, read_advancement.parent_id); + + let display = advancement.display.unwrap(); + let read_display = read_advancement.display.unwrap(); + assert_eq!(display.title, read_display.title); + assert_eq!(display.description, read_display.description); + } +} diff --git a/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs b/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs index 61fcbfbe..95f49a23 100755 --- a/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs @@ -1,7 +1,8 @@ -use azalea_buf::McBuf; +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ServerboundGamePacket; -#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +#[derive(Clone, Debug, McBuf, ServerboundGamePacket, PartialEq, Eq)] pub struct ServerboundClientInformationPacket { /// The locale of the client. pub language: String, @@ -14,7 +15,7 @@ pub struct ServerboundClientInformationPacket { /// Whether the messages sent from the server should have colors. Note that /// many servers ignore this and always send colored messages. pub chat_colors: bool, - pub model_customisation: u8, + pub model_customisation: ModelCustomization, pub main_hand: HumanoidArm, pub text_filtering_enabled: bool, /// Whether the client should show up as "Anonymous Player" in the server @@ -27,9 +28,9 @@ impl Default for ServerboundClientInformationPacket { Self { language: "en_us".to_string(), view_distance: 8, - chat_visibility: ChatVisibility::Full, + chat_visibility: ChatVisibility::default(), chat_colors: true, - model_customisation: 0, + model_customisation: ModelCustomization::default(), main_hand: HumanoidArm::Right, text_filtering_enabled: false, allows_listing: false, @@ -37,9 +38,10 @@ impl Default for ServerboundClientInformationPacket { } } -#[derive(McBuf, Clone, Copy, Debug)] +#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum ChatVisibility { /// All chat messages should be sent to the client. + #[default] Full = 0, /// Chat messages from other players should be not sent to the client, only /// messages from the server like "Player joined the game" should be sent. @@ -48,8 +50,125 @@ pub enum ChatVisibility { Hidden = 2, } -#[derive(McBuf, Clone, Copy, Debug)] +#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum HumanoidArm { Left = 0, + #[default] Right = 1, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ModelCustomization { + pub cape: bool, + pub jacket: bool, + pub left_sleeve: bool, + pub right_sleeve: bool, + pub left_pants: bool, + pub right_pants: bool, + pub hat: bool, +} + +impl Default for ModelCustomization { + fn default() -> Self { + Self { + cape: true, + jacket: true, + left_sleeve: true, + right_sleeve: true, + left_pants: true, + right_pants: true, + hat: true, + } + } +} + +impl McBufReadable for ModelCustomization { + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result { + let set = FixedBitSet::<7>::read_from(buf)?; + Ok(Self { + cape: set.index(0), + jacket: set.index(1), + left_sleeve: set.index(2), + right_sleeve: set.index(3), + left_pants: set.index(4), + right_pants: set.index(5), + hat: set.index(6), + }) + } +} + +impl McBufWritable for ModelCustomization { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + let mut set = FixedBitSet::<7>::new(); + if self.cape { + set.set(0); + } + if self.jacket { + set.set(1); + } + if self.left_sleeve { + set.set(2); + } + if self.right_sleeve { + set.set(3); + } + if self.left_pants { + set.set(4); + } + if self.right_pants { + set.set(5); + } + if self.hat { + set.set(6); + } + set.write_into(buf) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn test_client_information_packet() { + { + let data = ServerboundClientInformationPacket::default(); + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + + let read_data = + ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap(); + assert_eq!(read_data, data); + } + + { + let data = ServerboundClientInformationPacket { + language: "en_gb".to_string(), + view_distance: 24, + chat_visibility: ChatVisibility::Hidden, + chat_colors: false, + model_customisation: ModelCustomization { + cape: false, + jacket: false, + left_sleeve: true, + right_sleeve: false, + left_pants: true, + right_pants: false, + hat: true, + }, + main_hand: HumanoidArm::Left, + text_filtering_enabled: true, + allows_listing: true, + }; + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + + let read_data = + ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap(); + assert_eq!(read_data, data); + } + } +} diff --git a/azalea-protocol/src/packets/game/serverbound_interact_packet.rs b/azalea-protocol/src/packets/game/serverbound_interact_packet.rs index 1904accf..a117ca6f 100755 --- a/azalea-protocol/src/packets/game/serverbound_interact_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_interact_packet.rs @@ -63,9 +63,9 @@ impl McBufReadable for ActionType { let hand = InteractionHand::read_from(buf)?; Ok(ActionType::InteractAt { location: Vec3 { - x: x as f64, - y: y as f64, - z: z as f64, + x: f64::from(x), + y: f64::from(y), + z: f64::from(z), }, hand, }) diff --git a/azalea-protocol/src/packets/game/serverbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/serverbound_player_abilities_packet.rs index 7f979363..1f8727ac 100755 --- a/azalea-protocol/src/packets/game/serverbound_player_abilities_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_player_abilities_packet.rs @@ -1,29 +1,29 @@ use crate::packets::BufReadError; use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ServerboundGamePacket; use std::io::Cursor; #[derive(Clone, Debug, ServerboundGamePacket)] pub struct ServerboundPlayerAbilitiesPacket { - is_flying: bool, + pub is_flying: bool, } impl McBufReadable for ServerboundPlayerAbilitiesPacket { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; + let set = FixedBitSet::<2>::read_from(buf)?; Ok(Self { - is_flying: byte & 2 != 0, + is_flying: set.index(1), }) } } impl McBufWritable for ServerboundPlayerAbilitiesPacket { fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - let mut byte = 0; + let mut set = FixedBitSet::<2>::new(); if self.is_flying { - byte |= 2; + set.set(1); } - u8::write_into(&byte, buf)?; - Ok(()) + set.write_into(buf) } } diff --git a/azalea-protocol/src/packets/game/serverbound_player_input_packet.rs b/azalea-protocol/src/packets/game/serverbound_player_input_packet.rs index 2bd7f6eb..f0c7c548 100755 --- a/azalea-protocol/src/packets/game/serverbound_player_input_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_player_input_packet.rs @@ -2,6 +2,7 @@ use std::io::Cursor; use azalea_buf::BufReadError; use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; use azalea_protocol_macros::ServerboundGamePacket; #[derive(Clone, Debug, ServerboundGamePacket)] @@ -16,14 +17,12 @@ impl McBufReadable for ServerboundPlayerInputPacket { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let xxa = f32::read_from(buf)?; let zza = f32::read_from(buf)?; - let byte = u8::read_from(buf)?; - let is_jumping = byte & 1 != 0; - let is_shift_key_down = byte & 2 != 0; + let set = FixedBitSet::<2>::read_from(buf)?; Ok(Self { xxa, zza, - is_jumping, - is_shift_key_down, + is_jumping: set.index(0), + is_shift_key_down: set.index(1), }) } } @@ -32,14 +31,13 @@ impl McBufWritable for ServerboundPlayerInputPacket { fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { self.xxa.write_into(buf)?; self.zza.write_into(buf)?; - let mut byte = 0u8; + let mut set = FixedBitSet::<2>::new(); if self.is_jumping { - byte |= 1; + set.set(0); } if self.is_shift_key_down { - byte |= 2; + set.set(1); } - byte.write_into(buf)?; - Ok(()) + set.write_into(buf) } } diff --git a/azalea-protocol/src/packets/game/serverbound_set_command_block_packet.rs b/azalea-protocol/src/packets/game/serverbound_set_command_block_packet.rs index 7edb9719..d73654c2 100755 --- a/azalea-protocol/src/packets/game/serverbound_set_command_block_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_set_command_block_packet.rs @@ -1,6 +1,6 @@ use crate::packets::McBufWritable; use azalea_buf::{BufReadError, McBuf, McBufReadable}; -use azalea_core::BlockPos; +use azalea_core::{BlockPos, FixedBitSet}; use azalea_protocol_macros::ServerboundGamePacket; use std::io::Cursor; @@ -28,17 +28,14 @@ impl McBufReadable for ServerboundSetCommandBlockPacket { let command = String::read_from(buf)?; let mode = Mode::read_from(buf)?; - let byte = u8::read_from(buf)?; - let track_output = byte & 1 != 0; - let conditional = byte & 2 != 0; - let automatic = byte & 4 != 0; + let set = FixedBitSet::<3>::read_from(buf)?; Ok(Self { pos, command, mode, - track_output, - conditional, - automatic, + track_output: set.index(0), + conditional: set.index(1), + automatic: set.index(2), }) } } @@ -49,17 +46,16 @@ impl McBufWritable for ServerboundSetCommandBlockPacket { self.command.write_into(buf)?; self.mode.write_into(buf)?; - let mut byte: u8 = 0; + let mut set = FixedBitSet::<3>::new(); if self.track_output { - byte |= 1; + set.set(0); } if self.conditional { - byte |= 2; + set.set(1); } if self.automatic { - byte |= 4; + set.set(2); } - byte.write_into(buf)?; - Ok(()) + set.write_into(buf) } } diff --git a/azalea-protocol/src/packets/game/serverbound_set_structure_block_packet.rs b/azalea-protocol/src/packets/game/serverbound_set_structure_block_packet.rs index 901d3f89..28cc0035 100755 --- a/azalea-protocol/src/packets/game/serverbound_set_structure_block_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_set_structure_block_packet.rs @@ -1,7 +1,7 @@ use crate::packets::BufReadError; use azalea_buf::McBuf; use azalea_buf::{McBufReadable, McBufWritable}; -use azalea_core::BlockPos; +use azalea_core::{BlockPos, FixedBitSet}; use azalea_protocol_macros::ServerboundGamePacket; use std::io::{Cursor, Write}; @@ -69,28 +69,28 @@ pub struct Flags { impl McBufReadable for Flags { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let byte = u8::read_from(buf)?; + let set = FixedBitSet::<3>::read_from(buf)?; Ok(Self { - ignore_entities: byte & 1 != 0, - show_air: byte & 2 != 0, - show_bounding_box: byte & 4 != 0, + ignore_entities: set.index(0), + show_air: set.index(1), + show_bounding_box: set.index(2), }) } } impl McBufWritable for Flags { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut byte = 0; + let mut set = FixedBitSet::<3>::new(); if self.ignore_entities { - byte |= 1; + set.set(0); } if self.show_air { - byte |= 2; + set.set(1); } if self.show_bounding_box { - byte |= 4; + set.set(2); } - u8::write_into(&byte, buf)?; + set.write_into(buf)?; Ok(()) } } diff --git a/azalea-protocol/src/packets/game/serverbound_use_item_on_packet.rs b/azalea-protocol/src/packets/game/serverbound_use_item_on_packet.rs index ded977aa..17b537b6 100755 --- a/azalea-protocol/src/packets/game/serverbound_use_item_on_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_use_item_on_packet.rs @@ -24,9 +24,9 @@ impl McBufWritable for BlockHitResult { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { self.block_pos.write_into(buf)?; self.direction.write_into(buf)?; - f32::write_into(&((self.location.x - (self.block_pos.x as f64)) as f32), buf)?; - f32::write_into(&((self.location.y - (self.block_pos.y as f64)) as f32), buf)?; - f32::write_into(&((self.location.z - (self.block_pos.z as f64)) as f32), buf)?; + f32::write_into(&((self.location.x - f64::from(self.block_pos.x)) as f32), buf)?; + f32::write_into(&((self.location.y - f64::from(self.block_pos.y)) as f32), buf)?; + f32::write_into(&((self.location.z - f64::from(self.block_pos.z)) as f32), buf)?; self.inside.write_into(buf)?; Ok(()) } @@ -44,9 +44,9 @@ impl McBufReadable for BlockHitResult { block_pos, direction, location: Vec3 { - x: block_pos.x as f64 + cursor_x as f64, - y: block_pos.y as f64 + cursor_y as f64, - z: block_pos.z as f64 + cursor_z as f64, + x: f64::from(block_pos.x) + f64::from(cursor_x), + y: f64::from(block_pos.y) + f64::from(cursor_y), + z: f64::from(block_pos.z) + f64::from(cursor_z), }, inside, }) diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index 508d06a6..e803e85f 100755 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -21,6 +21,7 @@ pub enum ConnectionProtocol { } impl ConnectionProtocol { + #[must_use] pub fn from_i32(i: i32) -> Option { match i { -1 => Some(ConnectionProtocol::Handshake), @@ -39,7 +40,7 @@ where { fn id(&self) -> u32; - /// Read a packet by its id, ConnectionProtocol, and flow + /// Read a packet by its id, `ConnectionProtocol`, and flow fn read(id: u32, buf: &mut Cursor<&[u8]>) -> Result>; fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs index 7f81cfef..753717b9 100755 --- a/azalea-protocol/src/read.rs +++ b/azalea-protocol/src/read.rs @@ -76,8 +76,8 @@ pub enum FrameSplitterError { ConnectionClosed, } -/// Read a length, then read that amount of bytes from BytesMut. If there's not -/// enough data, return None +/// Read a length, then read that amount of bytes from `BytesMut`. If there's +/// not enough data, return None fn parse_frame(buffer: &mut BytesMut) -> Result { // copy the buffer first and read from the copy, then once we make sure // the packet is all good we read it fully @@ -258,41 +258,28 @@ where Ok(packet) } -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::packets::game::{clientbound_player_chat_packet::ChatType, -// ClientboundGamePacket}; use std::io::Cursor; +#[cfg(test)] +mod tests { + use super::*; + use crate::packets::game::ClientboundGamePacket; + use std::io::Cursor; -// #[tokio::test] -// async fn test_read_packet() { -// let mut buf: Cursor<&[u8]> = Cursor::new(&[ -// 51, 0, 12, 177, 250, 155, 132, 106, 60, 218, 161, 217, 90, 157, -// 105, 57, 206, 20, 0, 5, 104, 101, 108, 108, 111, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 116, 123, 34, 101, 120, -// 116, 114, 97, 34, 58, 91, 123, 34, 99, 111, 108, 111, 114, 34, 58, -// 34, 103, 114, 97, 121, 34, 44, 34, 116, 101, 120, 116, 34, 58, -// 34, 91, 77, 69, 77, 66, 69, 82, 93, 32, 112, 108, 97, 121, 101, -// 114, 49, 34, 125, 44, 123, 34, 116, 101, 120, 116, 34, 58, 34, -// 32, 34, 125, 44, 123, 34, 99, 111, 108, 111, 114, 34, 58, 34, 103, -// 114, 97, 121, 34, 44, 34, 116, 101, 120, 116, 34, 58, 34, 92, -// 117, 48, 48, 51, 101, 32, 104, 101, 108, 108, 111, 34, 125, 93, -// 44, 34, 116, 101, 120, 116, 34, 58, 34, 34, 125, 0, 7, 64, 123, -// 34, 101, 120, 116, 114, 97, 34, 58, 91, 123, 34, 99, 111, 108, 111, 114, -// 34, 58, 34, 103, 114, 97, 121, 34, 44, 34, 116, 101, 120, 116, -// 34, 58, 34, 91, 77, 69, 77, 66, 69, 82, 93, 32, 112, 108, 97, -// 121, 101, 114, 49, 34, 125, 93, 44, 34, 116, 101, 120, 116, 34, -// 58, 34, 34, 125, 0, ]); -// let packet = packet_decoder::(&mut -// buf).unwrap(); match &packet { -// ClientboundGamePacket::PlayerChat(m) => { -// assert_eq!( -// m.chat_type.chat_type, -// ChatType::Chat, -// "Enums should default if they're invalid" -// ); -// } -// _ => panic!("Wrong packet type"), -// } -// } -// } + #[tokio::test] + async fn test_read_packet() { + let mut buf: Cursor<&[u8]> = Cursor::new(&[ + 56, 64, 85, 58, 141, 138, 71, 146, 193, 64, 88, 0, 0, 0, 0, 0, 0, 64, 60, 224, 105, 34, + 119, 8, 228, 67, 50, 51, 68, 194, 177, 230, 101, 0, 17, 0, + ]); + let packet = packet_decoder::(&mut buf).unwrap(); + match &packet { + ClientboundGamePacket::PlayerPosition(p) => { + assert_eq!(p.id, 17); + assert_eq!(p.x, 84.91488892545296); + assert_eq!(p.y, 96.0); + assert_eq!(p.z, 28.876604227124417); + assert_eq!(p.dismount_vehicle, false); + } + _ => panic!("Wrong packet type"), + } + } +} diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs index dde5af3a..d12654f2 100755 --- a/azalea-protocol/src/resolver.rs +++ b/azalea-protocol/src/resolver.rs @@ -19,6 +19,7 @@ pub enum ResolverError { /// Resolve a Minecraft server address into an IP address and port. /// If it's already an IP address, it's returned as-is. +#[must_use] #[async_recursion] pub async fn resolve_address(address: &ServerAddress) -> Result { // If the address.host is already in the format of an ip address, return it.