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:
parent
127126c2cc
commit
a9ff79a105
15 changed files with 167 additions and 41 deletions
|
@ -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
|
||||||
|
|
|
@ -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>);
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
/// })
|
/// })
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
|
//! The Azalea prelude.
|
||||||
|
|
||||||
pub use crate::bot::BotTrait;
|
pub use crate::bot::BotTrait;
|
||||||
|
pub use azalea_client::{Account, Client, Event};
|
||||||
|
|
Loading…
Add table
Reference in a new issue