mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
204 lines
6.2 KiB
Rust
204 lines
6.2 KiB
Rust
#![doc = include_str!("../README.md")]
|
|
#![allow(incomplete_features)]
|
|
#![feature(type_changing_struct_update)]
|
|
#![feature(lazy_cell)]
|
|
#![feature(let_chains)]
|
|
#![feature(never_type)]
|
|
|
|
pub mod accept_resource_packs;
|
|
pub mod auto_respawn;
|
|
pub mod auto_tool;
|
|
mod bot;
|
|
pub mod container;
|
|
pub mod nearest_entity;
|
|
pub mod pathfinder;
|
|
pub mod prelude;
|
|
pub mod swarm;
|
|
|
|
use app::Plugins;
|
|
pub use azalea_auth as auth;
|
|
pub use azalea_block as blocks;
|
|
pub use azalea_brigadier as brigadier;
|
|
pub use azalea_buf as buf;
|
|
pub use azalea_chat::FormattedText;
|
|
pub use azalea_client::*;
|
|
pub use azalea_core as core;
|
|
// these are re-exported on this level because they're very common
|
|
pub use azalea_core::{
|
|
position::{BlockPos, Vec3},
|
|
resource_location::ResourceLocation,
|
|
};
|
|
pub use azalea_entity as entity;
|
|
pub use azalea_protocol as protocol;
|
|
pub use azalea_registry as registry;
|
|
pub use azalea_world as world;
|
|
pub use bot::*;
|
|
use ecs::component::Component;
|
|
use futures::{future::BoxFuture, Future};
|
|
use protocol::{resolver::ResolverError, ServerAddress};
|
|
use swarm::SwarmBuilder;
|
|
use thiserror::Error;
|
|
|
|
pub use bevy_app as app;
|
|
pub use bevy_ecs as ecs;
|
|
|
|
pub type BoxHandleFn<S> =
|
|
Box<dyn Fn(Client, azalea_client::Event, S) -> BoxFuture<'static, Result<(), anyhow::Error>>>;
|
|
pub type HandleFn<S, Fut> = fn(Client, azalea_client::Event, S) -> Fut;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum StartError {
|
|
#[error("Invalid address")]
|
|
InvalidAddress,
|
|
#[error(transparent)]
|
|
ResolveAddress(#[from] ResolverError),
|
|
}
|
|
|
|
/// A builder for creating new [`Client`]s. This is the recommended way of
|
|
/// making Azalea bots.
|
|
///
|
|
/// ```no_run
|
|
/// # use azalea::prelude::*;
|
|
/// # #[tokio::main]
|
|
/// # async fn main() {
|
|
/// ClientBuilder::new()
|
|
/// .set_handler(handle)
|
|
/// .start(Account::offline("bot"), "localhost")
|
|
/// .await;
|
|
/// # }
|
|
/// # #[derive(Component, Clone, Default)]
|
|
/// # pub struct State;
|
|
/// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub struct ClientBuilder<S>
|
|
where
|
|
S: Default + Send + Sync + Clone + Component + 'static,
|
|
{
|
|
/// Internally, ClientBuilder is just a wrapper over SwarmBuilder since it's
|
|
/// technically just a subset of it so we can avoid duplicating code this
|
|
/// way.
|
|
swarm: SwarmBuilder<S, swarm::NoSwarmState>,
|
|
}
|
|
impl ClientBuilder<NoState> {
|
|
/// Start building a client that can join the world.
|
|
#[must_use]
|
|
pub fn new() -> ClientBuilder<NoState> {
|
|
Self::new_without_plugins()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_plugins(DefaultBotPlugins)
|
|
}
|
|
|
|
/// [`Self::new`] but without adding the plugins by default. This is useful
|
|
/// if you want to disable a default plugin.
|
|
///
|
|
/// Note that you can also disable `LogPlugin` by disabling the `log`
|
|
/// feature.
|
|
///
|
|
/// You **must** add [`DefaultPlugins`] and [`DefaultBotPlugins`] to this.
|
|
///
|
|
/// ```
|
|
/// # use azalea::prelude::*;
|
|
/// use azalea::{app::PluginGroup, DefaultBotPlugins, DefaultPlugins};
|
|
/// use bevy_log::LogPlugin;
|
|
///
|
|
/// let client_builder = ClientBuilder::new_without_plugins()
|
|
/// .add_plugins(DefaultPlugins.build().disable::<LogPlugin>())
|
|
/// .add_plugins(DefaultBotPlugins);
|
|
/// # client_builder.set_handler(handle);
|
|
/// # #[derive(Component, Clone, Default)]
|
|
/// # pub struct State;
|
|
/// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
#[must_use]
|
|
pub fn new_without_plugins() -> ClientBuilder<NoState> {
|
|
Self {
|
|
swarm: SwarmBuilder::new_without_plugins(),
|
|
}
|
|
}
|
|
|
|
/// Set the function that's called every time a bot receives an [`Event`].
|
|
/// This is the way to handle normal per-bot events.
|
|
///
|
|
/// Currently you can have up to one client handler.
|
|
///
|
|
/// ```
|
|
/// # use azalea::prelude::*;
|
|
/// # let client_builder = azalea::ClientBuilder::new();
|
|
/// client_builder.set_handler(handle);
|
|
///
|
|
/// # #[derive(Component, Clone, Default)]
|
|
/// # pub struct State;
|
|
/// async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|
/// Ok(())
|
|
/// }
|
|
/// ```
|
|
#[must_use]
|
|
pub fn set_handler<S, Fut>(self, handler: HandleFn<S, Fut>) -> ClientBuilder<S>
|
|
where
|
|
S: Default + Send + Sync + Clone + Component + 'static,
|
|
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
|
|
{
|
|
ClientBuilder {
|
|
swarm: self.swarm.set_handler(handler),
|
|
}
|
|
}
|
|
}
|
|
impl<S> ClientBuilder<S>
|
|
where
|
|
S: Default + Send + Sync + Clone + Component + 'static,
|
|
{
|
|
/// Set the client state instead of initializing defaults.
|
|
#[must_use]
|
|
pub fn set_state(mut self, state: S) -> Self {
|
|
self.swarm.states = vec![state];
|
|
self
|
|
}
|
|
/// Add a group of plugins to the client.
|
|
#[must_use]
|
|
pub fn add_plugins<M>(mut self, plugins: impl Plugins<M>) -> Self {
|
|
self.swarm = self.swarm.add_plugins(plugins);
|
|
self
|
|
}
|
|
|
|
/// Build this `ClientBuilder` into an actual [`Client`] and join the given
|
|
/// server. If the client can't join, it'll keep retrying forever until it
|
|
/// can.
|
|
///
|
|
/// The `address` argument can be a `&str`, [`ServerAddress`], or anything
|
|
/// that implements `TryInto<ServerAddress>`.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This will error if the given address is invalid or couldn't be resolved
|
|
/// to a Minecraft server.
|
|
///
|
|
/// [`ServerAddress`]: azalea_protocol::ServerAddress
|
|
pub async fn start(
|
|
mut self,
|
|
account: Account,
|
|
address: impl TryInto<ServerAddress>,
|
|
) -> Result<!, StartError> {
|
|
self.swarm.accounts = vec![account];
|
|
if self.swarm.states.is_empty() {
|
|
self.swarm.states = vec![S::default()];
|
|
}
|
|
self.swarm.start(address).await
|
|
}
|
|
}
|
|
impl Default for ClientBuilder<NoState> {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// A marker that can be used in place of a State in [`ClientBuilder`] or
|
|
/// [`SwarmBuilder`]. You probably don't need to use this manually since the
|
|
/// compiler will infer it for you.
|
|
///
|
|
/// [`SwarmBuilder`]: swarm::SwarmBuilder
|
|
#[derive(Component, Clone, Default)]
|
|
pub struct NoState;
|