diff options
| -rw-r--r-- | azalea/src/lib.rs | 65 | ||||
| -rw-r--r-- | azalea/src/swarm/mod.rs | 185 |
2 files changed, 149 insertions, 101 deletions
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index f564cd9b..ce3494e2 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -2,6 +2,7 @@ #![feature(async_closure)] #![allow(incomplete_features)] #![feature(async_fn_in_trait)] +#![feature(type_changing_struct_update)] mod auto_respawn; mod bot; @@ -23,7 +24,7 @@ pub use azalea_registry::{Block, EntityKind, Item}; pub use azalea_world as world; pub use bot::DefaultBotPlugins; use ecs::component::Component; -use futures::Future; +use futures::{future::BoxFuture, Future}; use protocol::{ resolver::{self, ResolverError}, ServerAddress, @@ -34,7 +35,9 @@ use tokio::sync::mpsc; pub use bevy_app as app; pub use bevy_ecs as ecs; -pub type HandleFn<Fut, S> = fn(Client, azalea_client::Event, S) -> Fut; +pub type BoxHandleFn<S> = + Box<dyn Fn(Client, azalea_client::Event, S) -> BoxFuture<'static, Result<(), anyhow::Error>>>; +pub type HandleFn<S, Fut> = fn(Client, azalea_client::Event, S) -> Fut; #[derive(Error, Debug)] pub enum StartError { @@ -64,24 +67,19 @@ pub enum StartError { /// # Ok(()) /// # } /// ``` -pub struct ClientBuilder<S, Fut> +pub struct ClientBuilder<S> where - S: Default + Send + Sync + Clone + 'static, - Fut: Future<Output = Result<(), anyhow::Error>>, + S: Default + Send + Sync + Clone + Component + 'static, { app: App, /// The function that's called every time a bot receives an [`Event`]. - handler: Option<HandleFn<Fut, S>>, + handler: Option<BoxHandleFn<S>>, state: S, } -impl<S, Fut> ClientBuilder<S, Fut> -where - S: Default + Send + Sync + Clone + Component + 'static, - Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, -{ +impl ClientBuilder<NoState> { /// Start building a client that can join the world. #[must_use] - pub fn new() -> Self { + pub fn new() -> ClientBuilder<NoState> { Self::new_without_plugins() .add_plugins(DefaultPlugins) .add_plugins(DefaultBotPlugins) @@ -111,21 +109,20 @@ where /// # } /// ``` #[must_use] - pub fn new_without_plugins() -> Self { + pub fn new_without_plugins() -> ClientBuilder<NoState> { Self { // we create the app here so plugins can add onto it. // the schedules won't run until [`Self::start`] is called. app: App::new(), handler: None, - state: S::default(), + state: NoState, } } /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. /// - /// You must have exactly one client handler, calling this again will - /// replace the old client handler function. + /// Currently you can have up to one client handler. /// /// ``` /// # use azalea::prelude::*; @@ -139,10 +136,24 @@ where /// } /// ``` #[must_use] - pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self { - self.handler = Some(handler); - self + pub fn set_handler<S, Fut>(self, handler: HandleFn<S, Fut>) -> ClientBuilder<S> + where + S: Default + Send + Sync + Clone + Component + 'static, + Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, + { + ClientBuilder { + handler: Some(Box::new(move |bot, event, state| { + Box::pin(handler(bot, event, state)) + })), + state: S::default(), + ..self + } } +} +impl<S> ClientBuilder<S> +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 { @@ -186,7 +197,7 @@ where .await?; while let Some(event) = rx.recv().await { - if let Some(handler) = self.handler { + if let Some(handler) = &self.handler { tokio::spawn((handler)(bot.clone(), event.clone(), self.state.clone())); } } @@ -194,12 +205,16 @@ where Ok(()) } } -impl<S, Fut> Default for ClientBuilder<S, Fut> -where - S: Default + Send + Sync + Clone + Component + 'static, - Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, -{ +impl Default for ClientBuilder<NoState> { 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; diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 1ea4aa02..f84905ec 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -4,7 +4,6 @@ mod chat; mod events; pub mod prelude; -use crate::{bot::DefaultBotPlugins, HandleFn}; use azalea_client::{ chat::ChatPacket, start_ecs, Account, Client, DefaultPlugins, Event, JoinError, }; @@ -16,13 +15,15 @@ use azalea_protocol::{ use azalea_world::InstanceContainer; use bevy_app::{App, PluginGroup, PluginGroupBuilder, Plugins}; use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World}; -use futures::future::join_all; +use futures::future::{join_all, BoxFuture}; use log::error; use parking_lot::{Mutex, RwLock}; use std::{collections::HashMap, future::Future, net::SocketAddr, sync::Arc, time::Duration}; use thiserror::Error; use tokio::sync::mpsc; +use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, NoState}; + /// A swarm is a way to conveniently control many bots at once, while also /// being able to control bots at an individual level when desired. /// @@ -48,12 +49,10 @@ pub struct Swarm { } /// Create a new [`Swarm`]. -pub struct SwarmBuilder<S, SS, Fut, SwarmFut> +pub struct SwarmBuilder<S, SS> where - S: Send + Sync + Clone + 'static, - SS: Default + Send + Sync + Clone + 'static, - Fut: Future<Output = Result<(), anyhow::Error>>, - SwarmFut: Future<Output = Result<(), anyhow::Error>>, + S: Send + Sync + Clone + Component + 'static, + SS: Default + Send + Sync + Clone + Resource + 'static, { app: App, /// The accounts that are going to join the server. @@ -64,10 +63,10 @@ where /// The state for the overall swarm. swarm_state: SS, /// The function that's called every time a bot receives an [`Event`]. - handler: Option<HandleFn<Fut, S>>, + handler: Option<BoxHandleFn<S>>, /// The function that's called every time the swarm receives a /// [`SwarmEvent`]. - swarm_handler: Option<SwarmHandleFn<SwarmFut, SS>>, + swarm_handler: Option<BoxSwarmHandleFn<SS>>, /// How long we should wait between each bot joining the server. Set to /// None to have every bot connect at the same time. None is different than @@ -75,16 +74,10 @@ where /// the previous one to be ready. join_delay: Option<std::time::Duration>, } -impl<S, SS, Fut, SwarmFut> SwarmBuilder<S, SS, Fut, SwarmFut> -where - Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, - SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, - S: Send + Sync + Clone + Component + 'static, - SS: Default + Send + Sync + Clone + Resource + 'static, -{ +impl SwarmBuilder<NoState, NoSwarmState> { /// Start creating the swarm. #[must_use] - pub fn new() -> Self { + pub fn new() -> SwarmBuilder<NoState, NoSwarmState> { Self::new_without_plugins() .add_plugins(DefaultPlugins) .add_plugins(DefaultBotPlugins) @@ -117,62 +110,29 @@ where /// # } /// ``` #[must_use] - pub fn new_without_plugins() -> Self { - Self { + pub fn new_without_plugins() -> SwarmBuilder<NoState, NoSwarmState> { + SwarmBuilder { // we create the app here so plugins can add onto it. // the schedules won't run until [`Self::start`] is called. app: App::new(), - accounts: Vec::new(), states: Vec::new(), - swarm_state: SS::default(), + swarm_state: NoSwarmState, handler: None, swarm_handler: None, join_delay: None, } } +} - /// Add a vec of [`Account`]s to the swarm. - /// - /// Use [`Self::add_account`] to only add one account. If you want the - /// clients to have different default states, add them one at a time with - /// [`Self::add_account_with_state`]. - #[must_use] - pub fn add_accounts(mut self, accounts: Vec<Account>) -> Self - where - S: Default, - { - for account in accounts { - self = self.add_account(account); - } - self - } - /// Add a single new [`Account`] to the swarm. Use [`Self::add_accounts`] to - /// add multiple accounts at a time. - /// - /// This will make the state for this client be the default, use - /// [`Self::add_account_with_state`] to avoid that. - #[must_use] - pub fn add_account(self, account: Account) -> Self - where - S: Default, - { - self.add_account_with_state(account, S::default()) - } - /// Add an account with a custom initial state. Use just - /// [`Self::add_account`] to use the Default implementation for the state. - #[must_use] - pub fn add_account_with_state(mut self, account: Account, state: S) -> Self { - self.accounts.push(account); - self.states.push(state); - self - } - +impl<SS> SwarmBuilder<NoState, SS> +where + SS: Default + Send + Sync + Clone + Resource + 'static, +{ /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. /// - /// You must have exactly one client handler and one swarm handler, calling - /// this again will replace the old client handler function. + /// Currently you can have up to one handler. /// /// ``` /// # use azalea::{prelude::*, swarm::prelude::*}; @@ -196,15 +156,30 @@ where /// # } /// ``` #[must_use] - pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self { - self.handler = Some(handler); - self + pub fn set_handler<S, Fut>(self, handler: HandleFn<S, Fut>) -> SwarmBuilder<S, SS> + where + Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, + S: Send + Sync + Clone + Component + 'static, + { + SwarmBuilder { + handler: Some(Box::new(move |bot, event, state: S| { + Box::pin(handler(bot, event, state)) + })), + states: Vec::new(), + app: self.app, + ..self + } } +} + +impl<S> SwarmBuilder<S, NoSwarmState> +where + S: Send + Sync + Clone + Component + 'static, +{ /// Set the function that's called every time the swarm receives a /// [`SwarmEvent`]. This is the way to handle global swarm events. /// - /// You must have exactly one client handler and one swarm handler, calling - /// this again will replace the old swarm handler function. + /// Currently you can have up to one swarm handler. /// /// ``` /// # use azalea::{prelude::*, swarm::prelude::*}; @@ -229,10 +204,66 @@ where /// } /// ``` #[must_use] - pub fn set_swarm_handler(mut self, handler: SwarmHandleFn<SwarmFut, SS>) -> Self { - self.swarm_handler = Some(handler); + pub fn set_swarm_handler<SS, Fut>(self, handler: SwarmHandleFn<SS, Fut>) -> SwarmBuilder<S, SS> + where + SS: Default + Send + Sync + Clone + Resource + 'static, + Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, + { + SwarmBuilder { + handler: self.handler, + app: self.app, + accounts: self.accounts, + states: self.states, + swarm_state: SS::default(), + swarm_handler: Some(Box::new(move |swarm, event, state| { + Box::pin(handler(swarm, event, state)) + })), + join_delay: self.join_delay, + } + } +} + +impl<S, SS> SwarmBuilder<S, SS> +where + S: Send + Sync + Clone + Component + 'static, + SS: Default + Send + Sync + Clone + Resource + 'static, +{ + /// Add a vec of [`Account`]s to the swarm. + /// + /// Use [`Self::add_account`] to only add one account. If you want the + /// clients to have different default states, add them one at a time with + /// [`Self::add_account_with_state`]. + #[must_use] + pub fn add_accounts(mut self, accounts: Vec<Account>) -> Self + where + S: Default, + { + for account in accounts { + self = self.add_account(account); + } + self + } + /// Add a single new [`Account`] to the swarm. Use [`Self::add_accounts`] to + /// add multiple accounts at a time. + /// + /// This will make the state for this client be the default, use + /// [`Self::add_account_with_state`] to avoid that. + #[must_use] + pub fn add_account(self, account: Account) -> Self + where + S: Default, + { + self.add_account_with_state(account, S::default()) + } + /// Add an account with a custom initial state. Use just + /// [`Self::add_account`] to use the Default implementation for the state. + #[must_use] + pub fn add_account_with_state(mut self, account: Account, state: S) -> Self { + self.accounts.push(account); + self.states.push(state); self } + /// Set the swarm state instead of initializing defaults. #[must_use] pub fn set_swarm_state(mut self, swarm_state: SS) -> Self { @@ -344,7 +375,7 @@ where let swarm_clone = swarm.clone(); tokio::spawn(async move { while let Some(event) = swarm_rx.recv().await { - if let Some(swarm_handler) = self.swarm_handler { + if let Some(swarm_handler) = &self.swarm_handler { tokio::spawn((swarm_handler)( swarm_clone.clone(), event, @@ -356,7 +387,7 @@ where // bot events while let Some((Some(event), bot)) = bots_rx.recv().await { - if let Some(handler) = self.handler { + if let Some(handler) = &self.handler { let state = bot.component::<S>(); tokio::spawn((handler)(bot, event, state)); } @@ -368,13 +399,7 @@ where } } -impl<S, SS, Fut, SwarmFut> Default for SwarmBuilder<S, SS, Fut, SwarmFut> -where - Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, - SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, - S: Default + Send + Sync + Clone + Component + 'static, - SS: Default + Send + Sync + Clone + Resource + 'static, -{ +impl Default for SwarmBuilder<NoState, NoSwarmState> { fn default() -> Self { Self::new() } @@ -397,7 +422,9 @@ pub enum SwarmEvent { Chat(ChatPacket), } -pub type SwarmHandleFn<Fut, SS> = fn(Swarm, SwarmEvent, SS) -> Fut; +pub type SwarmHandleFn<SS, Fut> = fn(Swarm, SwarmEvent, SS) -> Fut; +pub type BoxSwarmHandleFn<SS> = + Box<dyn Fn(Swarm, SwarmEvent, SS) -> BoxFuture<'static, Result<(), anyhow::Error>> + Send>; #[derive(Error, Debug)] pub enum SwarmStartError { @@ -598,3 +625,9 @@ impl PluginGroup for DefaultSwarmPlugins { .add(events::SwarmPlugin) } } + +/// A marker that can be used in place of a SwarmState in [`SwarmBuilder`]. You +/// probably don't need to use this manually since the compiler will infer it +/// for you. +#[derive(Resource, Clone, Default)] +pub struct NoSwarmState; |
