From 65da123631b0a2dc078786f60fa6b213e8b430ee Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Sun, 23 Oct 2022 19:00:24 -0500 Subject: [PATCH] Add Client::set_client_information (#33) * start adding options * add default options * send options packet by default * mention set_options in Client::join doc * make TranslatableComponent::read return TextComponent * change set_options to set_client_information * clean up some code * Add `Initialize` event * fix some clippy warnings * change `Client::options` to `client_information` --- azalea-auth/src/auth.rs | 6 +-- azalea-buf/src/write.rs | 1 - azalea-chat/src/text_component.rs | 0 azalea-chat/src/translatable_component.rs | 6 +-- azalea-client/src/chat.rs | 4 +- azalea-client/src/client.rs | 51 ++++++++++++++++++- azalea-client/src/lib.rs | 2 +- azalea-protocol/src/connect.rs | 2 +- .../serverbound_client_information_packet.rs | 28 ++++++++++ azalea-world/src/chunk_storage.rs | 3 +- azalea/examples/craft_dig_straight_down.rs | 4 +- azalea/examples/echo.rs | 4 +- azalea/examples/potatobot/main.rs | 2 +- azalea/src/lib.rs | 2 +- azalea/src/prelude.rs | 2 +- bot/src/main.rs | 3 ++ 16 files changed, 97 insertions(+), 23 deletions(-) mode change 100755 => 100644 azalea-chat/src/text_component.rs mode change 100755 => 100644 azalea-chat/src/translatable_component.rs diff --git a/azalea-auth/src/auth.rs b/azalea-auth/src/auth.rs index 3e5f82d8..0d7c7ace 100644 --- a/azalea-auth/src/auth.rs +++ b/azalea-auth/src/auth.rs @@ -56,11 +56,7 @@ pub enum AuthError { /// though, and in case the Microsoft API does start providing the real email. pub async fn auth(email: &str, opts: AuthOpts) -> Result { let cached_account = if let Some(cache_file) = &opts.cache_file { - if let Some(account) = cache::get_account_in_cache(cache_file, email).await { - Some(account) - } else { - None - } + cache::get_account_in_cache(cache_file, email).await } else { None }; diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs index e1f1ffb1..e00ddce9 100644 --- a/azalea-buf/src/write.rs +++ b/azalea-buf/src/write.rs @@ -110,7 +110,6 @@ impl McBufWritable for String { } } - impl McBufWritable for &str { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { write_utf_with_len(buf, self, MAX_STRING_LENGTH.into()) diff --git a/azalea-chat/src/text_component.rs b/azalea-chat/src/text_component.rs old mode 100755 new mode 100644 diff --git a/azalea-chat/src/translatable_component.rs b/azalea-chat/src/translatable_component.rs old mode 100755 new mode 100644 index 750f65c4..918c2c42 --- a/azalea-chat/src/translatable_component.rs +++ b/azalea-chat/src/translatable_component.rs @@ -59,7 +59,7 @@ impl TranslatableComponent { .args .get(matched) .cloned() - .unwrap_or(StringOrComponent::String("".to_string())); + .unwrap_or_else(|| StringOrComponent::String("".to_string())); components.push(TextComponent::new(built_text.clone())); built_text.clear(); @@ -107,7 +107,7 @@ impl TranslatableComponent { Ok(TextComponent { base: BaseComponent { - siblings: components.into_iter().map(|c| Component::Text(c)).collect(), + siblings: components.into_iter().map(Component::Text).collect(), style: Style::default(), }, text: "".to_string(), @@ -135,7 +135,7 @@ impl Display for StringOrComponent { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { match self { StringOrComponent::String(s) => write!(f, "{}", s), - StringOrComponent::Component(c) => write!(f, "{}", c.to_string()), + StringOrComponent::Component(c) => write!(f, "{}", c), } } } diff --git a/azalea-client/src/chat.rs b/azalea-client/src/chat.rs index 335261e6..18fa4549 100644 --- a/azalea-client/src/chat.rs +++ b/azalea-client/src/chat.rs @@ -84,8 +84,8 @@ impl Client { /// # } /// ``` pub async fn chat(&self, message: &str) -> Result<(), std::io::Error> { - if message.starts_with('/') { - self.send_command_packet(&message[1..]).await + if let Some(command) = message.strip_prefix('/') { + self.send_command_packet(command).await } else { self.send_chat_packet(message).await } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index af47b2cd..60f9c9fc 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -9,6 +9,7 @@ use azalea_protocol::{ clientbound_player_chat_packet::ClientboundPlayerChatPacket, clientbound_system_chat_packet::ClientboundSystemChatPacket, serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket, + serverbound_client_information_packet::ServerboundClientInformationPacket, serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, serverbound_keep_alive_packet::ServerboundKeepAlivePacket, serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, @@ -30,7 +31,7 @@ use azalea_world::{ Dimension, }; use log::{debug, error, warn}; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use std::{ fmt::Debug, io::{self, Cursor}, @@ -43,10 +44,17 @@ use tokio::{ time::{self}, }; +pub type ClientInformation = ServerboundClientInformationPacket; + /// Events are sent before they're processed, so for example game ticks happen /// at the beginning of a tick before anything has happened. #[derive(Debug, Clone)] pub enum Event { + /// Happens right after the bot switches into the Game state, but before + /// it's actually spawned. This can be useful for setting the client + /// information with `Client::set_client_information`, so the packet + /// doesn't have to be sent twice. + Initialize, Login, Chat(ChatPacket), /// Happens 20 times per second, but only when the world is loaded. @@ -78,6 +86,7 @@ pub struct Client { pub player: Arc>, pub dimension: Arc>, pub physics_state: Arc>, + pub client_information: Arc>, tasks: Arc>>>, } @@ -123,6 +132,8 @@ pub enum HandleError { impl Client { /// Connect to a Minecraft server. /// + /// To change the render distance and other settings, use [`Client::set_client_information`]. + /// /// # Examples /// /// ```rust,no_run @@ -240,8 +251,11 @@ impl Client { dimension: Arc::new(Mutex::new(Dimension::default())), physics_state: Arc::new(Mutex::new(PhysicsState::default())), tasks: Arc::new(Mutex::new(Vec::new())), + client_information: Arc::new(RwLock::new(ClientInformation::default())), }; + tx.send(Event::Initialize).unwrap(); + // just start up the game loop and we're ready! // if you get an error right here that means you're doing something with locks wrong @@ -389,6 +403,12 @@ impl Client { player_lock.set_entity_id(p.player_id); } + // send the client information that we have set + let client_information_packet: ClientInformation = + client.client_information.read().clone(); + client.write_packet(client_information_packet.get()).await?; + + // brand client .write_packet( ServerboundCustomPayloadPacket { @@ -806,6 +826,35 @@ impl Client { .entity(entity_id) .expect("Player entity should be in the given dimension") } + + /// Returns whether we have a received the login packet yet. + pub fn logged_in(&self) -> bool { + let dimension = self.dimension.lock(); + let player = self.player.lock(); + player.entity(&dimension).is_some() + } + + /// Tell the server we changed our game options (i.e. render distance, main hand). + /// If this is not set before the login packet, the default will be sent. + pub async fn set_client_information( + &self, + client_information: ServerboundClientInformationPacket, + ) -> Result<(), std::io::Error> { + { + let mut client_information_lock = self.client_information.write(); + *client_information_lock = client_information; + } + + if self.logged_in() { + let client_information_packet = { + let client_information = self.client_information.read(); + client_information.clone().get() + }; + self.write_packet(client_information_packet).await?; + } + + Ok(()) + } } impl From> for HandleError { diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 61d2d9ee..84af0298 100755 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -14,7 +14,7 @@ pub mod ping; mod player; pub use account::Account; -pub use client::{Client, Event}; +pub use client::{Client, ClientInformation, Event}; pub use movement::MoveDirection; pub use player::Player; diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index 39e34948..da398ff9 100644 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -166,7 +166,7 @@ impl Connection { /// `ClientboundLoginPacket::Hello` packet. /// /// # Examples - /// + /// /// ```rust,no_run /// let token = azalea_auth::auth(azalea_auth::AuthOpts { /// ..Default::default() 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 0c1ab14b..61fcbfbe 100644 --- a/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs @@ -3,20 +3,48 @@ use azalea_protocol_macros::ServerboundGamePacket; #[derive(Clone, Debug, McBuf, ServerboundGamePacket)] pub struct ServerboundClientInformationPacket { + /// The locale of the client. pub language: String, + /// The view distance of the client in chunks, same as the render distance + /// in-game. pub view_distance: u8, + /// The types of chat messages the client wants to receive. Note that many + /// servers ignore this. pub chat_visibility: ChatVisibility, + /// 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 main_hand: HumanoidArm, pub text_filtering_enabled: bool, + /// Whether the client should show up as "Anonymous Player" in the server + /// list. pub allows_listing: bool, } +impl Default for ServerboundClientInformationPacket { + fn default() -> Self { + Self { + language: "en_us".to_string(), + view_distance: 8, + chat_visibility: ChatVisibility::Full, + chat_colors: true, + model_customisation: 0, + main_hand: HumanoidArm::Right, + text_filtering_enabled: false, + allows_listing: false, + } + } +} + #[derive(McBuf, Clone, Copy, Debug)] pub enum ChatVisibility { + /// All chat messages should be sent to the client. 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. System = 1, + /// No chat messages should be sent to the client. Hidden = 2, } diff --git a/azalea-world/src/chunk_storage.rs b/azalea-world/src/chunk_storage.rs index daabb214..0c60376a 100644 --- a/azalea-world/src/chunk_storage.rs +++ b/azalea-world/src/chunk_storage.rs @@ -106,7 +106,8 @@ impl ChunkStorage { if !self.in_range(pos) { log::trace!( "Ignoring chunk since it's not in the view range: {}, {}", - pos.x, pos.z + pos.x, + pos.z ); return Ok(()); } diff --git a/azalea/examples/craft_dig_straight_down.rs b/azalea/examples/craft_dig_straight_down.rs index 9e675f28..82960a9c 100644 --- a/azalea/examples/craft_dig_straight_down.rs +++ b/azalea/examples/craft_dig_straight_down.rs @@ -1,5 +1,5 @@ -use azalea::{pathfinder, Account}; -use azalea::{Bot, Client, Event}; +use azalea::pathfinder; +use azalea::prelude::*; use parking_lot::Mutex; use std::sync::Arc; diff --git a/azalea/examples/echo.rs b/azalea/examples/echo.rs index 07dd50c1..8c11d6e7 100644 --- a/azalea/examples/echo.rs +++ b/azalea/examples/echo.rs @@ -1,8 +1,6 @@ //! A simple bot that repeats chat messages sent by other players. -use azalea::{Account, Client, Event}; -use parking_lot::Mutex; -use std::sync::Arc; +use azalea::prelude::*; #[tokio::main] async fn main() { diff --git a/azalea/examples/potatobot/main.rs b/azalea/examples/potatobot/main.rs index 1aa28ecc..54c84570 100644 --- a/azalea/examples/potatobot/main.rs +++ b/azalea/examples/potatobot/main.rs @@ -1,7 +1,7 @@ mod autoeat; use azalea::prelude::*; -use azalea::{pathfinder, Account, BlockPos, Client, Event, ItemKind, MoveDirection, Plugin, Vec3}; +use azalea::{pathfinder, BlockPos, ItemKind, Vec3}; #[derive(Default, Clone)] struct State {} diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 013e2dd3..a7d0791a 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -129,7 +129,7 @@ pub enum Error { /// it gets disconnected from the server. /// /// # Examples -/// +/// /// ```rust,no_run /// let error = azalea::start(azalea::Options { /// account, diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index 0ffb60b8..c09d85e2 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -1,4 +1,4 @@ -//! The Azalea prelude. +//! The Azalea prelude. Things that are necessary for a bare-bones bot are re-exported here. pub use crate::bot::BotTrait; pub use azalea_client::{Account, Client, Event}; diff --git a/bot/src/main.rs b/bot/src/main.rs index 9f8bd0c8..e4543b6a 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -28,6 +28,9 @@ async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> Event::Login => { bot.chat("Hello world").await?; } + Event::Initialize => { + println!("initialized"); + } Event::Tick => { bot.jump(); }