aboutsummaryrefslogtreecommitdiff
path: root/azalea/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea/src')
-rw-r--r--azalea/src/bot.rs42
-rw-r--r--azalea/src/lib.rs82
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(())
+}