From 2eade86cf7a12a6ec64496aedbfc3d3a3bd44e1a Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 23 Oct 2022 16:51:49 -0500 Subject: make `handle` cleaner Arc -> Event, Arc> -> State Items in State now need to have interior mutability (i.e. Arc>), but it's a worthwhile tradeoff since it allows the user to customize it for each field --- azalea/src/bot.rs | 19 +++++++++--------- azalea/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 25 deletions(-) (limited to 'azalea/src') diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index 26e35fda..566ab1e7 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -3,14 +3,14 @@ use async_trait::async_trait; use parking_lot::Mutex; use std::sync::Arc; -#[derive(Default)] +#[derive(Default, Clone)] pub struct Plugin { - pub state: Arc>, + pub state: State, } -#[derive(Default)] +#[derive(Default, Clone)] pub struct State { - jumping_once: bool, + jumping_once: Arc>, } pub trait BotTrait { @@ -18,7 +18,7 @@ pub trait BotTrait { } impl BotTrait for azalea_client::Client { - /// Try to jump next tick. + /// Queue a jump for the next tick. fn jump(&self) { let player_lock = self.player.lock(); let mut dimension_lock = self.dimension.lock(); @@ -33,12 +33,11 @@ impl BotTrait for azalea_client::Client { #[async_trait] impl crate::Plugin for Plugin { - async fn handle(self: Arc, event: Arc, mut bot: Client) { - if let Event::Tick = *event { - let mut state = self.state.lock(); - if state.jumping_once { + async fn handle(self: Box, event: Event, mut bot: Client) { + if let Event::Tick = event { + if *self.state.jumping_once.lock() { if bot.jumping() { - state.jumping_once = false; + *self.state.jumping_once.lock() = false; } else { bot.set_jumping(true); } diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 19384761..6a000e6d 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -30,6 +30,7 @@ //! .unwrap(); //! } //! +//! #[derive(Default, Clone)] //! pub struct State {} //! //! async fn handle(bot: Client, event: Arc, state: Arc>) -> anyhow::Result<()> { @@ -52,17 +53,35 @@ 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 std::future::Future; 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, event: Arc, bot: Client); +pub trait Plugin: Send + Sync + PluginClone + 'static { + async fn handle(self: Box, event: Event, bot: Client); } -pub type HandleFn = fn(Client, Arc, Arc>) -> Fut; +/// An internal trait that allows Plugin to be cloned. +#[doc(hidden)] +pub trait PluginClone { + fn clone_box(&self) -> Box; +} +impl PluginClone for T +where + T: 'static + Plugin + Clone, +{ + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_box() + } +} + +pub type HandleFn = fn(Client, Event, S) -> Fut; /// The options that are passed to [`azalea::start`]. /// @@ -80,10 +99,22 @@ where pub account: Account, /// A list of plugins that are going to be used. Plugins are external /// crates that add extra functionality to Azalea. - pub plugins: Vec>, + pub plugins: Vec>, /// A struct that contains the data that you want your bot to remember /// across events. - pub state: Arc>, + /// + /// # Examples + /// + /// ```rust + /// use parking_lot::Mutex; + /// use std::sync::Arc; + /// + /// #[derive(Default, Clone)] + /// struct State { + /// farming: Arc>, + /// } + /// ``` + pub state: S, /// The function that's called whenever we get an event. pub handle: HandleFn, } @@ -107,7 +138,7 @@ pub enum Error { /// }).await.unwrap(); /// ``` pub async fn start< - S: Send + 'static, + S: Send + Sync + Clone + 'static, A: Send + TryInto, Fut: Future> + Send + 'static, >( @@ -121,19 +152,16 @@ pub async fn start< let (bot, mut rx) = Client::join(&options.account, address).await.unwrap(); let state = options.state; - let bot_plugin = Arc::new(bot::Plugin::default()); + let bot_plugin = 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(event.clone(), bot.clone())); + let plugin = plugin.clone(); + tokio::spawn(plugin.handle(event.clone(), bot.clone())); } tokio::spawn(bot::Plugin::handle( - bot_plugin.clone(), + Box::new(bot_plugin.clone()), event.clone(), bot.clone(), )); -- cgit v1.2.3