1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
#![doc = include_str!("../README.md")]
#![feature(async_closure)]
mod bot;
pub mod pathfinder;
pub mod prelude;
mod swarm;
pub use azalea_block as blocks;
pub use azalea_client::*;
pub use azalea_core::{BlockPos, Vec3};
use azalea_ecs::{
app::{App, Plugin},
component::Component,
};
pub use azalea_protocol as protocol;
pub use azalea_registry::EntityKind;
pub use azalea_world::{entity, World};
use bot::DefaultBotPlugins;
use ecs::app::PluginGroup;
use futures::Future;
use protocol::{
resolver::{self, ResolverError},
ServerAddress,
};
pub use swarm::*;
use thiserror::Error;
use tokio::sync::mpsc;
pub type HandleFn<Fut, S> = fn(Client, Event, S) -> Fut;
#[derive(Error, Debug)]
pub enum StartError {
#[error("Invalid address")]
InvalidAddress,
#[error(transparent)]
ResolveAddress(#[from] ResolverError),
#[error("Join error: {0}")]
Join(#[from] azalea_client::JoinError),
}
/// A builder for creating new [`Client`]s. This is the recommended way of
/// making Azalea bots.
///
/// ```no_run
/// azalea::ClientBuilder::new()
/// .set_handler(handle)
/// .start(Account::offline("bot"), "localhost")
/// .await;
/// ```
pub struct ClientBuilder<S, Fut>
where
S: Default + Send + Sync + Clone + 'static,
Fut: Future<Output = Result<(), anyhow::Error>>,
{
app: App,
/// The function that's called every time a bot receives an [`Event`].
handler: Option<HandleFn<Fut, S>>,
state: S,
}
impl<S, Fut> ClientBuilder<S, Fut>
where
S: Default + Send + Sync + Clone + Component + 'static,
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
{
/// Start building a client that can join the world.
#[must_use]
pub fn new() -> Self {
Self {
// we create the app here so plugins can add onto it.
// the schedules won't run until [`Self::start`] is called.
app: init_ecs_app(),
handler: None,
state: S::default(),
}
.add_plugins(DefaultBotPlugins)
}
/// Set the function that's called every time a bot receives an [`Event`].
/// This is the way to handle normal per-bot events.
///
/// You can only have one client handler, calling this again will replace
/// the old client handler function (you can have a client handler and swarm
/// handler separately though).
#[must_use]
pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self {
self.handler = Some(handler);
self
}
/// Add a plugin to the client.
#[must_use]
pub fn add_plugin<T: Plugin>(mut self, plugin: T) -> Self {
self.app.add_plugin(plugin);
self
}
/// Add a group of plugins to the client.
#[must_use]
pub fn add_plugins<T: PluginGroup>(mut self, plugin_group: T) -> Self {
self.app.add_plugins(plugin_group);
self
}
/// Build this `ClientBuilder` into an actual [`Client`] and join the given
/// server.
///
/// The `address` argument can be a `&str`, [`ServerAddress`], or anything
/// that implements `TryInto<ServerAddress>`.
///
/// [`ServerAddress`]: azalea_protocol::ServerAddress
pub async fn start(
self,
account: Account,
address: impl TryInto<ServerAddress>,
) -> Result<(), StartError> {
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
let resolved_address = resolver::resolve_address(&address).await?;
// An event that causes the schedule to run. This is only used internally.
let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
let ecs_lock = start_ecs(self.app, run_schedule_receiver, run_schedule_sender.clone());
let (bot, mut rx) = Client::start_client(
ecs_lock,
&account,
&address,
&resolved_address,
run_schedule_sender,
)
.await?;
while let Some(event) = rx.recv().await {
if let Some(handler) = self.handler {
tokio::spawn((handler)(bot.clone(), event.clone(), self.state.clone()));
}
}
Ok(())
}
}
impl<S, Fut> Default for ClientBuilder<S, Fut>
where
S: Default + Send + Sync + Clone + Component + 'static,
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
{
fn default() -> Self {
Self::new()
}
}
|