mod bot; pub mod prelude; use async_trait::async_trait; pub use azalea_client::*; use azalea_protocol::ServerAddress; use parking_lot::Mutex; use std::{future::Future, sync::Arc}; use thiserror::Error; /// Plugins can keep their own personal state, listen to events, and add new functions to Client. #[async_trait] pub trait Plugin: Send + Sync { async fn handle(self: Arc, bot: Client, event: Arc); } // pub type HeuristicFn = fn(start: &Vertex, current: &Vertex) -> W; pub type HandleFn = fn(Client, Arc, Arc>) -> Fut; pub struct Options where A: TryInto, Fut: Future>, { pub address: A, pub account: Account, pub plugins: Vec>, pub state: Arc>, pub handle: HandleFn, } #[derive(Error, Debug)] pub enum Error { #[error("Invalid address")] InvalidAddress, } /// Join a Minecraft server. /// /// ```no_run /// azalea::start(azalea::Options { /// account, /// address: "localhost", /// state: Arc::new(Mutex::new(State::default())), /// plugins: vec![&autoeat::Plugin::default()], /// handle: Box::new(handle), /// }).await.unwrap(); /// ``` pub async fn start< S: Send + 'static, A: Send + TryInto, Fut: Future> + Send + 'static, >( options: Options, ) -> Result<(), Error> { let address = match options.address.try_into() { Ok(address) => address, Err(_) => return Err(Error::InvalidAddress), }; let (bot, mut rx) = options.account.join(&address).await.unwrap(); let state = options.state; let bot_plugin = Arc::new(bot::Plugin::default()); while let Some(event) = rx.recv().await { // we put it into an Arc so it's cheaper to clone let event = Arc::new(event); for plugin in &options.plugins { tokio::spawn(plugin.clone().handle(bot.clone(), event.clone())); } { let bot_plugin = bot_plugin.clone(); let bot = bot.clone(); let event = event.clone(); tokio::spawn(bot::Plugin::handle(bot_plugin, bot, event)); }; tokio::spawn((options.handle)(bot.clone(), event.clone(), state.clone())); } Ok(()) }