diff options
Diffstat (limited to 'azalea/src')
| -rw-r--r-- | azalea/src/bot.rs | 42 | ||||
| -rw-r--r-- | azalea/src/lib.rs | 82 |
2 files changed, 118 insertions, 6 deletions
diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index 6746e09e..a77e2a1c 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -1,14 +1,46 @@ -pub struct BotState { +use crate::{Client, Event}; +use async_trait::async_trait; +use parking_lot::Mutex; +use std::sync::Arc; + +#[derive(Default)] +pub struct Plugin { + pub state: Arc<Mutex<State>>, +} + +#[derive(Default)] +pub struct State { jumping_once: bool, } pub trait BotTrait { - fn jump(&mut self); + fn jump(&self); } impl BotTrait for azalea_client::Client { - fn jump(&mut self) { - let mut physics_state = self.physics_state.lock().unwrap(); - physics_state.jumping_once = true; + /// Try to jump next tick. + fn jump(&self) { + let player_lock = self.player.lock(); + let mut dimension_lock = self.dimension.lock(); + + let mut player_entity = player_lock + .entity_mut(&mut dimension_lock) + .expect("Player must exist"); + + player_entity.jumping = true; + } +} + +#[async_trait] +impl crate::Plugin for Plugin { + async fn handle(self: Arc<Self>, mut bot: Client, event: Arc<Event>) { + if let Event::Tick = *event { + let mut state = self.state.lock(); + if bot.jumping() { + state.jumping_once = false; + } else if state.jumping_once { + bot.set_jumping(true); + } + } } } diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index fe8a3740..8ef02e7c 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -1,4 +1,84 @@ mod bot; pub mod prelude; -pub use azalea_client::Client; +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<Self>, bot: Client, event: Arc<Event>); +} + +// pub type HeuristicFn<N, W> = fn(start: &Vertex<N, W>, current: &Vertex<N, W>) -> W; +pub type HandleFn<Fut, S> = fn(Client, Arc<Event>, Arc<Mutex<S>>) -> Fut; + +pub struct Options<S, A, Fut> +where + A: TryInto<ServerAddress>, + Fut: Future<Output = Result<(), anyhow::Error>>, +{ + pub address: A, + pub account: Account, + pub plugins: Vec<Arc<dyn Plugin>>, + pub state: Arc<Mutex<S>>, + pub handle: HandleFn<Fut, S>, +} + +#[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<ServerAddress>, + Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static, +>( + options: Options<S, A, Fut>, +) -> 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(()) +} |
