New example (#24)

the example isn't finished but it's finished enough
This commit is contained in:
mat 2022-10-02 14:58:42 -05:00 committed by GitHub
parent 37f9f1c6fe
commit 06068377bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 163 additions and 26 deletions

14
Cargo.lock generated
View file

@ -101,6 +101,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "azalea"
version = "0.1.0"
dependencies = [
"anyhow",
"env_logger",
"tokio",
]
[[package]]
name = "azalea-auth"
@ -890,9 +895,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
@ -1432,10 +1437,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.19.2"
version = "1.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",

View file

@ -66,7 +66,10 @@ mod tests {
#[test]
fn test_from_blockstate() {
let box_block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::Air);
assert_eq!(box_block.id(), "air");
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::Air);
assert_eq!(block.id(), "air");
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::FloweringAzalea);
assert_eq!(block.id(), "flowering_azalea");
}
}

View file

@ -9,3 +9,9 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dev-dependencies]
tokio = "^1.21.1"
env_logger = "^0.9.1"
anyhow = "^1.0.65"

View file

@ -30,12 +30,11 @@ async fn handle_event(event: &Event, bot: &Bot, ctx: Arc<Context>) {
ctx_lock.started = true;
drop(ctx_lock);
bot.goto_goal(
bot.goto(
pathfinder::Goals::NearXZ(5, azalea::BlockXZ(0, 0))
).await;
let chest = bot.open_container(&bot.world.find_one_block(|b| b.id == "minecraft:chest")).await.unwrap();
bot.take_amount(&chest, 5, |i| i.id == "#minecraft:planks").await;
// when rust adds async drop this won't be necessary
chest.close().await;
let crafting_table = bot.open_crafting_table(&bot.world.find_one_block(|b| b.id == "minecraft:crafting_table")).await.unwrap();

View file

@ -12,15 +12,15 @@ async fn main() {
let bots = accounts.join("localhost".try_into().unwrap()).await.unwrap();
bots.goto(azalea::BlockCoord(0, 70, 0)).await;
// or bots.goto_goal(pathfinder::Goals::Goto(azalea::BlockCoord(0, 70, 0))).await;
bots.goto(azalea::BlockPos::new(0, 70, 0)).await;
// or bots.goto_goal(pathfinder::Goals::Goto(azalea::BlockPos(0, 70, 0))).await;
// destroy the blocks in this area and then leave
bots.fill(
azalea::Selection::Range(
azalea::BlockCoord(0, 0, 0),
azalea::BlockCoord(16, 255, 16)
azalea::BlockPos::new(0, 0, 0),
azalea::BlockPos::new(16, 255, 16)
),
azalea::block::Air
).await;

View file

@ -0,0 +1,24 @@
A relatively complex bot for farming potatoes.
Note: At the moment, all of the code here is only hypothetical. I decided to write this to help me decide how I want some the APIs to look.
## Attempted
- Sync: a sync function is called with the state and bot every time we get an event, and the function can queue events to execute at the end of the tick
Pros: No .lock().unwrap() necessary, and theoretically pausable by saving the state.
Cons: Async functions like opening containers and pathfinding are annoying because you have to keep state for them, and the code generally ends up being more confusing.
- Async non-blocking: an async function is called in a new task with the state mutex and bot every time we get an event
Pros: Easier to do async stuff like interacting with containers, code is somewhat easier to understand
Cons: Lock spam everywhere is annoying, and you have to make sure stuff doesn't accidentally run in parallel.
## Considered:
(I didn't actually try this because the problems were apparent)
- Async blocking: an async function is called with the state and bot every time we get an event, and only handles the next event when this one finishes running
Pros: No lock spam
Cons: Sometimes you want to handle multiple events at once like eating if you get hungry while pathfinding, this makes it harder without increasing complexity

View file

@ -0,0 +1,20 @@
//! Automatically eat when we get hungry.
use azalea::{Client, Event};
use std::sync::{Arc, Mutex};
#[derive(Default)]
pub struct State {}
pub async fn handle(bot: &mut Client, event: Event, state: Arc<Mutex<State>>) {
match event {
Event::UpdateHunger => {
if !bot.using_held_item() && bot.food_level() <= 17 {
if bot.hold(azalea::ItemGroup::Food).await {
bot.use_held_item().await;
}
}
}
_ => {}
}
}

View file

@ -0,0 +1,91 @@
mod autoeat;
use azalea::{pathfinder, Account, BlockPos, Client, Event, ItemKind, MoveDirection, Vec3};
use std::{
convert::TryInto,
sync::{Arc, Mutex},
};
#[derive(Default)]
struct State {
pub eating: bool,
}
#[tokio::main]
async fn main() {
env_logger::init();
let account = Account::offline("bot");
let (bot, mut rx) = account
.join(&"localhost".try_into().unwrap())
.await
.unwrap();
// Maybe all this could be turned into a macro in the future?
let state = Arc::new(Mutex::new(State::default()));
let autoeat_state = Arc::new(Mutex::new(autoeat::State::default()));
let pathfinder_state = Arc::new(Mutex::new(pathfinder::State::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);
tokio::spawn(autoeat::handle(
bot.clone(),
event.clone(),
autoeat_state.clone(),
));
tokio::spawn(pathfinder::handle(
bot.clone(),
event.clone(),
pathfinder_state.clone(),
));
tokio::spawn(handle(bot.clone(), event.clone(), state.clone()));
}
}
async fn handle(bot: Client, event: Event, state: Arc<Mutex<State>>) -> anyhow::Result<()> {
match event {
Event::Login => {
goto_farm(bot, state).await?;
// after we get to the farm, start farming
farm(bot, state).await?;
}
_ => {}
}
Ok(())
}
// go to the place where we start farming
async fn goto_farm(bot: Client, state: Arc<Mutex<State>>) -> anyhow::Result<()> {
bot.state
.goto(pathfinder::Goals::Near(5, BlockPos::new(0, 70, 0)))
.await?;
Ok(())
}
// go to the chest and deposit everything in our inventory.
async fn deposit(bot: &mut Client, state: &mut Arc<Mutex<State>>) -> anyhow::Result<()> {
// first throw away any garbage we might have
bot.toss(|item| item.kind != ItemKind::Potato && item.kind != ItemKind::DiamondHoe);
bot.state.goto(Vec3::new(0, 70, 0)).await?;
let chest = bot
.open_container(&bot.dimension.block_at(BlockPos::new(0, 70, 0)))
.await
.unwrap();
let inventory_potato_count: usize = bot
.inventory()
.count_total(|item| item.kind == ItemKind::Potato);
if inventory_potato_count > 64 {
chest
.deposit_total_count(
|item| item.kind == azalea::ItemKind::Potato,
inventory_potato_count - 64,
)
.await;
}
chest.close().await;
Ok(())
}

View file

@ -1,14 +1,2 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
//! This is currently an advertisement crate for
//! [Azalea](https://github.com/mat-1/azalea). More stuff will be here soon!