1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

write more documentation

This commit is contained in:
mat 2022-10-23 14:46:06 -05:00
parent 127126c2cc
commit a9ff79a105
15 changed files with 167 additions and 41 deletions

View file

@ -48,7 +48,7 @@ pub enum AuthError {
GetXboxLiveAuth(#[from] XboxLiveAuthError), GetXboxLiveAuth(#[from] XboxLiveAuthError),
} }
/// Authenticate with authenticate with Microsoft. If the data isn't cached, /// Authenticate with Microsoft. If the data isn't cached,
/// they'll be asked to go to log into Microsoft in a web page. /// they'll be asked to go to log into Microsoft in a web page.
/// ///
/// The email is technically only used as a cache key, so it *could* be /// The email is technically only used as a cache key, so it *could* be

View file

@ -1,6 +1,6 @@
use std::ops::Deref; use std::ops::Deref;
/// A Vec<u8> that isn't prefixed by a VarInt with the size. /// A `Vec<u8>` that isn't prefixed by a VarInt with the size.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnsizedByteArray(pub Vec<u8>); pub struct UnsizedByteArray(pub Vec<u8>);

View file

@ -1,13 +1,14 @@
//! Connect to Minecraft servers. //! Connect to Minecraft servers.
use crate::{client::JoinError, get_mc_dir, Client, Event}; use crate::get_mc_dir;
use azalea_protocol::ServerAddress;
use tokio::sync::mpsc::UnboundedReceiver;
use uuid::Uuid; use uuid::Uuid;
/// Something that can join Minecraft servers. /// Something that can join Minecraft servers.
///
/// To join a server using this account, use [`crate::Client::join`].
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Account { pub struct Account {
/// The Minecraft username of the account.
pub username: String, pub username: String,
/// The access token for authentication. You can obtain one of these /// The access token for authentication. You can obtain one of these
/// manually from azalea-auth. /// manually from azalea-auth.
@ -15,7 +16,11 @@ pub struct Account {
/// Only required for online-mode accounts. /// Only required for online-mode accounts.
pub uuid: Option<uuid::Uuid>, pub uuid: Option<uuid::Uuid>,
} }
impl Account { impl Account {
/// An offline account does not authenticate with Microsoft's servers, and
/// as such can only join offline mode servers. This is useful for testing
/// in LAN worlds.
pub fn offline(username: &str) -> Self { pub fn offline(username: &str) -> Self {
Self { Self {
username: username.to_string(), username: username.to_string(),
@ -24,6 +29,10 @@ impl Account {
} }
} }
/// This will create an online-mode account by authenticating with
/// Microsoft's servers. Note that the email given is actually only used as
/// a key for the cache, but it's recommended to use the real email to
/// avoid confusion.
pub async fn microsoft(email: &str) -> Result<Self, azalea_auth::AuthError> { pub async fn microsoft(email: &str) -> Result<Self, azalea_auth::AuthError> {
let minecraft_dir = get_mc_dir::minecraft_dir().unwrap(); let minecraft_dir = get_mc_dir::minecraft_dir().unwrap();
let auth_result = azalea_auth::auth( let auth_result = azalea_auth::auth(
@ -40,12 +49,4 @@ impl Account {
uuid: Some(Uuid::parse_str(&auth_result.profile.id).expect("Invalid UUID")), uuid: Some(Uuid::parse_str(&auth_result.profile.id).expect("Invalid UUID")),
}) })
} }
/// Joins the Minecraft server on the given address using this account.
pub async fn join(
&self,
address: &ServerAddress,
) -> Result<(Client, UnboundedReceiver<Event>), JoinError> {
Client::join(self, address).await
}
} }

View file

@ -11,8 +11,9 @@ use crate::Client;
impl Client { impl Client {
/// Sends chat message to the server. This only sends the chat packet and /// Sends chat message to the server. This only sends the chat packet and
/// not the command packet. The `chat` function handles checking whether /// not the command packet. The [`Client::chat`] function handles checking whether
/// the message is a command and using the proper packet for you. /// the message is a command and using the proper packet for you, so you
/// should use that instead.
pub async fn send_chat_packet(&self, message: &str) -> Result<(), std::io::Error> { pub async fn send_chat_packet(&self, message: &str) -> Result<(), std::io::Error> {
// TODO: chat signing // TODO: chat signing
let signature = sign_message(); let signature = sign_message();
@ -54,6 +55,33 @@ impl Client {
self.write_packet(packet).await self.write_packet(packet).await
} }
/// Send a message in chat.
///
/// # Examples
///
/// ```rust,no_run
/// # use azalea::prelude::*;
/// # use parking_lot::Mutex;
/// # use std::sync::Arc;
/// # #[tokio::main]
/// # async fn main() {
/// # let account = Account::offline("bot");
/// # azalea::start(azalea::Options {
/// # account,
/// # address: "localhost",
/// # state: Arc::new(Mutex::new(State::default())),
/// # plugins: vec![],
/// # handle,
/// # })
/// # .await
/// # .unwrap();
/// # }
/// # pub struct State {}
/// # async fn handle(bot: Client, event: Arc<Event>, state: Arc<Mutex<State>>) -> anyhow::Result<()> {
/// bot.chat("Hello, world!").await.unwrap();
/// # Ok(())
/// # }
/// ```
pub async fn chat(&self, message: &str) -> Result<(), std::io::Error> { pub async fn chat(&self, message: &str) -> Result<(), std::io::Error> {
if message.chars().next() == Some('/') { if message.chars().next() == Some('/') {
self.send_command_packet(&message[1..]).await self.send_command_packet(&message[1..]).await

View file

@ -69,7 +69,7 @@ impl ChatPacket {
} }
} }
/// A player that you can control that is currently in a Minecraft server. /// A player that you control that is currently in a Minecraft server.
#[derive(Clone)] #[derive(Clone)]
pub struct Client { pub struct Client {
game_profile: GameProfile, game_profile: GameProfile,
@ -106,6 +106,8 @@ pub enum JoinError {
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("{0}")] #[error("{0}")]
SessionServer(#[from] azalea_auth::sessionserver::SessionServerError), SessionServer(#[from] azalea_auth::sessionserver::SessionServerError),
#[error("The given address could not be parsed into a ServerAddress")]
InvalidAddress,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -119,12 +121,26 @@ pub enum HandleError {
} }
impl Client { impl Client {
/// Connect to a Minecraft server with an account. /// Connect to a Minecraft server.
///
/// ```rust,no_run
/// use azalea_client::Client;
///
/// #[tokio::main]
/// async fn main() -> Box<dyn std::error::Error> {
/// let account = Account::offline("bot");
/// let client = Client::join(&account, "localhost").await?;
/// client.chat("Hello, world!").await?;
/// client.shutdown().await?;
/// }
/// ```
pub async fn join( pub async fn join(
account: &Account, account: &Account,
address: &ServerAddress, address: impl TryInto<ServerAddress>,
) -> Result<(Self, UnboundedReceiver<Event>), JoinError> { ) -> Result<(Self, UnboundedReceiver<Event>), JoinError> {
let resolved_address = resolver::resolve_address(address).await?; let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
let resolved_address = resolver::resolve_address(&address).await?;
let mut conn = Connection::new(&resolved_address).await?; let mut conn = Connection::new(&resolved_address).await?;

View file

@ -1,4 +1,9 @@
//! Significantly abstract azalea-protocol so it's actually useable for bots. //! Significantly abstract [`azalea_protocol`] so it's actually useable for
//! real clients. If you want to make bots, however, you should use the
//! [`azalea`] crate instead.
//!
//! [`azalea_protocol`]: https://crates.io/crates/azalea-protocol
//! [`azalea`]: https://crates.io/crates/azalea
mod account; mod account;
mod chat; mod chat;

View file

@ -155,6 +155,8 @@ impl Client {
Ok(()) Ok(())
} }
/// Makes the bot do one physics tick. Note that this is already handled
/// automatically by the client.
pub fn ai_step(&mut self) { pub fn ai_step(&mut self) {
self.tick_controls(None); self.tick_controls(None);

View file

@ -1,4 +1,5 @@
///! Ping Minecraft servers. //! Ping Minecraft servers.
use azalea_protocol::{ use azalea_protocol::{
connect::{Connection, ConnectionError}, connect::{Connection, ConnectionError},
packets::{ packets::{
@ -25,12 +26,27 @@ pub enum PingError {
ReadPacket(#[from] azalea_protocol::read::ReadPacketError), ReadPacket(#[from] azalea_protocol::read::ReadPacketError),
#[error("{0}")] #[error("{0}")]
WritePacket(#[from] io::Error), WritePacket(#[from] io::Error),
#[error("The given address could not be parsed into a ServerAddress")]
InvalidAddress,
} }
/// Ping a Minecraft server.
///
/// ```
/// use azalea_client::ping;
///
/// #[tokio::main]
/// async fn main() {
/// let response = ping::ping_server("play.hypixel.net").await.unwrap();
/// println!("{}", response.description.to_ansi(None));
/// }
/// ```
pub async fn ping_server( pub async fn ping_server(
address: &ServerAddress, address: impl TryInto<ServerAddress>,
) -> Result<ClientboundStatusResponsePacket, PingError> { ) -> Result<ClientboundStatusResponsePacket, PingError> {
let resolved_address = resolver::resolve_address(address).await?; let address: ServerAddress = address.try_into().map_err(|_| PingError::InvalidAddress)?;
let resolved_address = resolver::resolve_address(&address).await?;
let mut conn = Connection::new(&resolved_address).await?; let mut conn = Connection::new(&resolved_address).await?;

View file

@ -165,7 +165,7 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
/// online-mode servers. This must happen when you get a /// online-mode servers. This must happen when you get a
/// `ClientboundLoginPacket::Hello` packet. /// `ClientboundLoginPacket::Hello` packet.
/// ///
/// ```no_run /// ```rust,no_run
/// let token = azalea_auth::auth(azalea_auth::AuthOpts { /// let token = azalea_auth::auth(azalea_auth::AuthOpts {
/// ..Default::default() /// ..Default::default()
/// }) /// })

View file

@ -1,3 +1,3 @@
A framework for creating Minecraft bots. Azalea is a framework for creating Minecraft bots.
Interally, it's just a wrapper over azalea-client, adding useful functions for making bots. Internally, it's just a wrapper over azalea-client, adding useful functions for making bots.

View file

@ -11,7 +11,7 @@ struct State {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let account = Account::offline("bot"); let account = Account::offline("bot");
// or let bot = Account::microsoft("access token").await; // or let bot = Account::microsoft("email").await;
azalea::start(azalea::Options { azalea::start(azalea::Options {
account, account,

View file

@ -1,12 +1,13 @@
use std::sync::Arc; //! A simple bot that repeats chat messages sent by other players.
use azalea::{Account, Client, Event}; use azalea::{Account, Client, Event};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let account = Account::offline("bot"); let account = Account::offline("bot");
// or let account = Account::microsoft("access token").await; // or let account = Account::microsoft("email").await;
azalea::start(azalea::Options { azalea::start(azalea::Options {
account, account,
@ -22,7 +23,7 @@ async fn main() {
pub struct State {} pub struct State {}
async fn handle(bot: Client, event: Arc<Event>, state: Arc<Mutex<State>>) -> anyhow::Result<()> { async fn handle(bot: Client, event: Arc<Event>, state: Arc<Mutex<State>>) -> anyhow::Result<()> {
match event { match *event {
Event::Chat(m) => { Event::Chat(m) => {
if m.username == bot.username { if m.username == bot.username {
return Ok(()); // ignore our own messages return Ok(()); // ignore our own messages
@ -33,12 +34,6 @@ async fn handle(bot: Client, event: Arc<Event>, state: Arc<Mutex<State>>) -> any
println!(m); println!(m);
bot.reconnect().await.unwrap(); bot.reconnect().await.unwrap();
} }
Event::HungerUpdate(h) => {
if !h.using_held_item() && h.hunger <= 17 {
bot.hold(azalea::ItemGroup::Food).await?;
bot.use_held_item().await?;
}
}
_ => {} _ => {}
} }

View file

@ -1,7 +1,6 @@
use std::sync::Arc;
use azalea::{pathfinder, Account, Accounts, Client, Event}; use azalea::{pathfinder, Account, Accounts, Client, Event};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {

View file

@ -1,3 +1,51 @@
//! Azalea is a framework for creating Minecraft bots.
//!
//! Internally, it's just a wrapper over [`azalea_client`], adding useful
//! functions for making bots. Because of this, lots of the documentation will
//! refer to `azalea_client`. You can just replace these with `azalea` in your
//! code, since everything from azalea_client is re-exported in azalea.
//!
//! # Examples
//!
//! ```rust,no_run
//! //! A bot that logs chat messages sent in the server to the console.
//!
//! use azalea::prelude::*;
//! use parking_lot::Mutex;
//! use std::sync::Arc;
//!
//! #[tokio::main]
//! async fn main() {
//! let account = Account::offline("bot");
//! // or Account::microsoft("example@example.com").await.unwrap();
//!
//! azalea::start(azalea::Options {
//! account,
//! address: "localhost",
//! state: Arc::new(Mutex::new(State::default())),
//! plugins: vec![],
//! handle,
//! })
//! .await
//! .unwrap();
//! }
//!
//! pub struct State {}
//!
//! async fn handle(bot: Client, event: Arc<Event>, state: Arc<Mutex<State>>) -> anyhow::Result<()> {
//! match *event {
//! Event::Chat(m) => {
//! println!(m.message().to_ansi(None));
//! }
//! _ => {}
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! [`azalea_client`]: https://crates.io/crates/azalea-client
mod bot; mod bot;
pub mod prelude; pub mod prelude;
@ -16,15 +64,27 @@ pub trait Plugin: Send + Sync {
pub type HandleFn<Fut, S> = fn(Client, Arc<Event>, Arc<Mutex<S>>) -> Fut; pub type HandleFn<Fut, S> = fn(Client, Arc<Event>, Arc<Mutex<S>>) -> Fut;
/// The options that are passed to [`azalea::start`].
///
/// [`azalea::start`]: fn.start.html
pub struct Options<S, A, Fut> pub struct Options<S, A, Fut>
where where
A: TryInto<ServerAddress>, A: TryInto<ServerAddress>,
Fut: Future<Output = Result<(), anyhow::Error>>, Fut: Future<Output = Result<(), anyhow::Error>>,
{ {
/// The address of the server that we're connecting to. This can be a
/// `&str`, [`ServerAddress`], or anything that implements
/// `TryInto<ServerAddress>`.
pub address: A, pub address: A,
/// The account that's going to join the server,
pub account: Account, 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<Arc<dyn Plugin>>, pub plugins: Vec<Arc<dyn Plugin>>,
/// A struct that contains the data that you want your bot to remember
/// across events.
pub state: Arc<Mutex<S>>, pub state: Arc<Mutex<S>>,
/// The function that's called whenever we get an event.
pub handle: HandleFn<Fut, S>, pub handle: HandleFn<Fut, S>,
} }
@ -34,9 +94,10 @@ pub enum Error {
InvalidAddress, InvalidAddress,
} }
/// Join a Minecraft server. /// Join a server and start handling events. This function will run forever until
/// it gets disconnected from the server.
/// ///
/// ```no_run /// ```rust,no_run
/// azalea::start(azalea::Options { /// azalea::start(azalea::Options {
/// account, /// account,
/// address: "localhost", /// address: "localhost",
@ -57,7 +118,7 @@ pub async fn start<
Err(_) => return Err(Error::InvalidAddress), Err(_) => return Err(Error::InvalidAddress),
}; };
let (bot, mut rx) = options.account.join(&address).await.unwrap(); let (bot, mut rx) = Client::join(&options.account, address).await.unwrap();
let state = options.state; let state = options.state;
let bot_plugin = Arc::new(bot::Plugin::default()); let bot_plugin = Arc::new(bot::Plugin::default());

View file

@ -1 +1,4 @@
//! The Azalea prelude.
pub use crate::bot::BotTrait; pub use crate::bot::BotTrait;
pub use azalea_client::{Account, Client, Event};