diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index f241d6c8..b69ebd06 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -470,7 +470,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let Some(default_state_id) = default_state_id else { let defaults = properties_with_name.iter().map(|p| if let TokenTree::Ident(i) = p.default.clone().into_iter().last().unwrap() { i.to_string() } else { panic!() }).collect::>(); - panic!("Couldn't get default state id for {}, combinations={:?}, defaults={:?}", block_name_pascal_case.to_string(), block_properties_vec, defaults) + panic!("Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}") }; // 7035..=7058 => { @@ -583,7 +583,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { block_structs.extend(block_struct); } - let last_state_id = (state_id - 1) as u32; + let last_state_id = state_id - 1; let mut generated = quote! { #property_enums diff --git a/azalea/README.md b/azalea/README.md index a9ef7094..ed3c595e 100755 --- a/azalea/README.md +++ b/azalea/README.md @@ -33,7 +33,6 @@ opt-level = 1 opt-level = 3 ``` - # Examples ```rust,no_run @@ -49,9 +48,9 @@ async fn main() { // or Account::microsoft("example@example.com").await.unwrap(); loop { - let e = azalea::ClientBuilder::new() + let e = ClientBuilder::new() .set_handler(handle) - .start(account, "localhost") + .start(account.clone(), "localhost") .await; eprintln!("{:?}", e); } @@ -72,6 +71,10 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> { } ``` +# Swarms + +Azalea lets you create "swarms", which are a group of bots in the same world that can perform actions together. See [testbot](https://github.com/mat-1/azalea/blob/main/azalea/examples/testbot.rs) for an example. + # Plugins Azalea uses [Bevy ECS](https://docs.rs/bevy_ecs) internally to store information about the world and clients. Bevy plugins are more powerful than async handler functions, but more difficult to use. See [pathfinder](azalea/src/pathfinder/mod.rs) as an example of how to make a plugin. You can then enable a plugin by adding `.add_plugin(ExamplePlugin)` in your client/swarm builder. diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 3fe86fa6..af236dc4 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -6,7 +6,7 @@ use azalea::ecs::query::With; use azalea::entity::metadata::Player; use azalea::entity::Position; use azalea::pathfinder::BlockPosGoal; -use azalea::{prelude::*, BlockPos, GameProfileComponent, Swarm, SwarmEvent, WalkDirection}; +use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection}; use azalea::{Account, Client, Event}; use azalea_protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket; use std::time::Duration; diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index b7707b92..a34ea179 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -4,7 +4,7 @@ mod bot; pub mod pathfinder; pub mod prelude; -mod swarm; +pub mod swarm; pub use azalea_block as blocks; pub use azalea_client::*; @@ -23,7 +23,6 @@ use protocol::{ resolver::{self, ResolverError}, ServerAddress, }; -pub use swarm::*; use thiserror::Error; use tokio::sync::mpsc; @@ -43,10 +42,19 @@ pub enum StartError { /// making Azalea bots. /// /// ```no_run -/// azalea::ClientBuilder::new() +/// # 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 @@ -79,9 +87,20 @@ where /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. /// - /// You can only have one client handler, calling this again will replace - /// the old client handler function (you can have a client handler and swarm - /// handler separately though). + /// You must have exactly one client handler, calling this again will + /// replace the old client handler function. + /// + /// ``` + /// # 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(mut self, handler: HandleFn) -> Self { self.handler = Some(handler); diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index a26f72d1..a9ae6093 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -1,9 +1,6 @@ //! The Azalea prelude. Things that are necessary for a bare-bones bot are //! re-exported here. -pub use crate::bot::BotClientExt; -pub use crate::pathfinder::PathfinderClientExt; -pub use crate::{ClientBuilder, SwarmBuilder}; +pub use crate::{bot::BotClientExt, pathfinder::PathfinderClientExt, ClientBuilder}; pub use azalea_client::{Account, Client, Event}; -pub use azalea_ecs::component::Component; -pub use azalea_ecs::system::Resource; +pub use azalea_ecs::{component::Component, system::Resource}; diff --git a/azalea/src/swarm/chat.rs b/azalea/src/swarm/chat.rs index a55e9bf6..8a00c34d 100644 --- a/azalea/src/swarm/chat.rs +++ b/azalea/src/swarm/chat.rs @@ -23,7 +23,7 @@ use azalea_ecs::{ }; use std::collections::VecDeque; -use crate::{Swarm, SwarmEvent}; +use super::{Swarm, SwarmEvent}; #[derive(Clone)] pub struct SwarmChatPlugin; diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 64312579..33402566 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -2,6 +2,7 @@ mod chat; mod events; +pub mod prelude; use crate::{bot::DefaultBotPlugins, HandleFn}; use azalea_client::{init_ecs_app, start_ecs, Account, ChatPacket, Client, Event, JoinError}; @@ -136,9 +137,30 @@ where /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. /// - /// You can only have one client handler, calling this again will replace - /// the old client handler function (you can have a client handler and swarm - /// handler separately though). + /// You must have exactly one client handler and one swarm handler, calling + /// this again will replace the old client handler function. + /// + /// ``` + /// # use azalea::{prelude::*, swarm::prelude::*}; + /// # let swarm_builder = SwarmBuilder::new().set_swarm_handler(swarm_handle); + /// swarm_builder.set_handler(handle); + /// + /// #[derive(Component, Default, Clone)] + /// struct State {} + /// async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { + /// Ok(()) + /// } + /// + /// # #[derive(Resource, Default, Clone)] + /// # struct SwarmState {} + /// # async fn swarm_handle( + /// # mut swarm: Swarm, + /// # event: SwarmEvent, + /// # state: SwarmState, + /// # ) -> anyhow::Result<()> { + /// # Ok(()) + /// # } + /// ``` #[must_use] pub fn set_handler(mut self, handler: HandleFn) -> Self { self.handler = Some(handler); @@ -147,9 +169,31 @@ where /// Set the function that's called every time the swarm receives a /// [`SwarmEvent`]. This is the way to handle global swarm events. /// - /// You can only have one swarm handler, calling this again will replace - /// the old swarm handler function (you can have a client handler and swarm - /// handler separately though). + /// You must have exactly one client handler and one swarm handler, calling + /// this again will replace the old swarm handler function. + /// + /// ``` + /// # use azalea::{prelude::*, swarm::prelude::*}; + /// # let swarm_builder = SwarmBuilder::new().set_handler(handle); + /// swarm_builder.set_swarm_handler(swarm_handle); + /// + /// # #[derive(Component, Default, Clone)] + /// # struct State {} + /// + /// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { + /// # Ok(()) + /// # } + /// + /// #[derive(Resource, Default, Clone)] + /// struct SwarmState {} + /// async fn swarm_handle( + /// mut swarm: Swarm, + /// event: SwarmEvent, + /// state: SwarmState, + /// ) -> anyhow::Result<()> { + /// Ok(()) + /// } + /// ``` #[must_use] pub fn set_swarm_handler(mut self, handler: SwarmHandleFn) -> Self { self.swarm_handler = Some(handler); @@ -337,7 +381,7 @@ pub enum SwarmStartError { /// /// # Examples /// ```rust,no_run -/// use azalea::{prelude::*, Swarm, SwarmEvent}; +/// use azalea::{prelude::*, swarm::prelude::*}; /// use std::time::Duration; /// /// #[derive(Default, Clone, Component)] @@ -376,7 +420,7 @@ pub enum SwarmStartError { /// } /// /// async fn swarm_handle( -/// mut swarm: Swarm, +/// mut swarm: Swarm, /// event: SwarmEvent, /// _state: SwarmState, /// ) -> anyhow::Result<()> { @@ -483,10 +527,15 @@ impl IntoIterator for Swarm { /// Iterate over the bots in this swarm. /// /// ```rust,no_run + /// # use azalea::{prelude::*, swarm::prelude::*}; + /// #[derive(Component, Clone)] + /// # pub struct State; + /// # fn example(swarm: Swarm) { /// for bot in swarm { /// let state = bot.component::(); /// // ... /// } + /// # } /// ``` fn into_iter(self) -> Self::IntoIter { self.bots