aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/swarm
diff options
context:
space:
mode:
authorTheDudeFromCI <thedudefromci@gmail.com>2023-08-15 22:12:37 -0700
committerGitHub <noreply@github.com>2023-08-16 00:12:37 -0500
commitf0ff8e7f295270795000f6a8fcd8c2230a3733f5 (patch)
treec71d8f4873731b1f18b5654adc8a35e5e0c01e10 /azalea/src/swarm
parent8a90a8e109b88c1c70b88f7488a0a8eb5f52d724 (diff)
downloadazalea-drasl-f0ff8e7f295270795000f6a8fcd8c2230a3733f5.tar.xz
no_handler() option for ClientBuilder (#100)
* Added no_handler client builder option Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Made EmptyState public Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Packaged no_handler placeholders in own module Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * imply no state and remove Fut generic from ClientBuilder and SwarmBuilder * use destructuring in set_handler and fix a doc --------- Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea/src/swarm')
-rw-r--r--azalea/src/swarm/mod.rs185
1 files changed, 109 insertions, 76 deletions
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;