#![doc = include_str!("../README.md")] #![allow(incomplete_features)] #![feature(type_changing_struct_update)] #![feature(lazy_cell)] #![feature(let_chains)] #![feature(never_type)] pub mod accept_resource_packs; pub mod auto_respawn; pub mod auto_tool; mod bot; pub mod container; pub mod nearest_entity; pub mod pathfinder; pub mod prelude; pub mod swarm; use app::Plugins; pub use azalea_auth as auth; pub use azalea_block as blocks; pub use azalea_brigadier as brigadier; pub use azalea_buf as buf; pub use azalea_chat::FormattedText; pub use azalea_client::*; pub use azalea_core as core; // these are re-exported on this level because they're very common pub use azalea_core::{ position::{BlockPos, Vec3}, resource_location::ResourceLocation, }; pub use azalea_entity as entity; pub use azalea_protocol as protocol; pub use azalea_registry as registry; pub use azalea_world as world; pub use bot::*; use ecs::component::Component; use futures::{future::BoxFuture, Future}; use protocol::{resolver::ResolverError, ServerAddress}; use swarm::SwarmBuilder; use thiserror::Error; pub use bevy_app as app; pub use bevy_ecs as ecs; pub type BoxHandleFn = Box BoxFuture<'static, Result<(), anyhow::Error>>>; pub type HandleFn = fn(Client, azalea_client::Event, S) -> Fut; #[derive(Error, Debug)] pub enum StartError { #[error("Invalid address")] InvalidAddress, #[error(transparent)] ResolveAddress(#[from] ResolverError), } /// A builder for creating new [`Client`]s. This is the recommended way of /// making Azalea bots. /// /// ```no_run /// # use azalea::prelude::*; /// # #[tokio::main] /// # async fn main() { /// ClientBuilder::new() /// .set_handler(handle) /// .start(Account::offline("bot"), "localhost") /// .await; /// # } /// # #[derive(Component, Clone, Default)] /// # pub struct State; /// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { /// # Ok(()) /// # } /// ``` pub struct ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, { /// Internally, ClientBuilder is just a wrapper over SwarmBuilder since it's /// technically just a subset of it so we can avoid duplicating code this /// way. swarm: SwarmBuilder, } impl ClientBuilder { /// Start building a client that can join the world. #[must_use] pub fn new() -> ClientBuilder { Self::new_without_plugins() .add_plugins(DefaultPlugins) .add_plugins(DefaultBotPlugins) } /// [`Self::new`] but without adding the plugins by default. This is useful /// if you want to disable a default plugin. /// /// Note that you can also disable `LogPlugin` by disabling the `log` /// feature. /// /// You **must** add [`DefaultPlugins`] and [`DefaultBotPlugins`] to this. /// /// ``` /// # use azalea::prelude::*; /// use azalea::{app::PluginGroup, DefaultBotPlugins, DefaultPlugins}; /// use bevy_log::LogPlugin; /// /// let client_builder = ClientBuilder::new_without_plugins() /// .add_plugins(DefaultPlugins.build().disable::()) /// .add_plugins(DefaultBotPlugins); /// # client_builder.set_handler(handle); /// # #[derive(Component, Clone, Default)] /// # pub struct State; /// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { /// # Ok(()) /// # } /// ``` #[must_use] pub fn new_without_plugins() -> ClientBuilder { Self { swarm: SwarmBuilder::new_without_plugins(), } } /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. /// /// Currently you can have up to one client handler. /// /// ``` /// # use azalea::prelude::*; /// # let client_builder = azalea::ClientBuilder::new(); /// client_builder.set_handler(handle); /// /// # #[derive(Component, Clone, Default)] /// # pub struct State; /// async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { /// Ok(()) /// } /// ``` #[must_use] pub fn set_handler(self, handler: HandleFn) -> ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, Fut: Future> + Send + 'static, { ClientBuilder { swarm: self.swarm.set_handler(handler), } } } impl ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, { /// Set the client state instead of initializing defaults. #[must_use] pub fn set_state(mut self, state: S) -> Self { self.swarm.states = vec![state]; self } /// Add a group of plugins to the client. #[must_use] pub fn add_plugins(mut self, plugins: impl Plugins) -> Self { self.swarm = self.swarm.add_plugins(plugins); self } /// Build this `ClientBuilder` into an actual [`Client`] and join the given /// server. If the client can't join, it'll keep retrying forever until it /// can. /// /// The `address` argument can be a `&str`, [`ServerAddress`], or anything /// that implements `TryInto`. /// /// # Errors /// /// This will error if the given address is invalid or couldn't be resolved /// to a Minecraft server. /// /// [`ServerAddress`]: azalea_protocol::ServerAddress pub async fn start( mut self, account: Account, address: impl TryInto, ) -> Result { self.swarm.accounts = vec![account]; if self.swarm.states.is_empty() { self.swarm.states = vec![S::default()]; } self.swarm.start(address).await } } impl Default for ClientBuilder { fn default() -> Self { Self::new() } } /// A marker that can be used in place of a State in [`ClientBuilder`] or /// [`SwarmBuilder`]. You probably don't need to use this manually since the /// compiler will infer it for you. /// /// [`SwarmBuilder`]: swarm::SwarmBuilder #[derive(Component, Clone, Default)] pub struct NoState;