aboutsummaryrefslogtreecommitdiff
path: root/azalea/examples
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-10-02 14:58:42 -0500
committerGitHub <noreply@github.com>2022-10-02 14:58:42 -0500
commit06068377bd17f95bdafe86ff14bab1d0d852aa53 (patch)
treeed3f15107d69dc0cc8f6794745832b82a1649c80 /azalea/examples
parent37f9f1c6feda676be30bef31291eaed2a5fc82ce (diff)
downloadazalea-drasl-06068377bd17f95bdafe86ff14bab1d0d852aa53.tar.xz
New example (#24)
the example isn't finished but it's finished enough
Diffstat (limited to 'azalea/examples')
-rw-r--r--azalea/examples/craft_dig_straight_down.rs56
-rw-r--r--azalea/examples/echo.rs38
-rw-r--r--azalea/examples/mine_a_chunk.rs27
-rw-r--r--azalea/examples/potatobot/README.md24
-rw-r--r--azalea/examples/potatobot/autoeat.rs20
-rw-r--r--azalea/examples/potatobot/main.rs91
-rw-r--r--azalea/examples/pvp.rs33
7 files changed, 289 insertions, 0 deletions
diff --git a/azalea/examples/craft_dig_straight_down.rs b/azalea/examples/craft_dig_straight_down.rs
new file mode 100644
index 00000000..53e5cae8
--- /dev/null
+++ b/azalea/examples/craft_dig_straight_down.rs
@@ -0,0 +1,56 @@
+use azalea::{Bot, Event};
+
+struct Context {
+ pub started: bool
+}
+
+#[tokio::main]
+async fn main() {
+ let bot = Bot::offline("bot");
+ // or let bot = azalea::Bot::microsoft("access token").await;
+
+ bot.join("localhost".try_into().unwrap()).await.unwrap();
+
+ let ctx = Arc::new(Mutex::new(Context { started: false }));
+
+ loop {
+ tokio::spawn(handle_event(bot.next().await, bot, ctx.clone()));
+ }
+}
+
+
+async fn handle_event(event: &Event, bot: &Bot, ctx: Arc<Context>) {
+ match event {
+ Event::Message(m) {
+ if m.username == bot.player.username { return };
+ if m.message = "go" {
+ // make sure we only start once
+ let ctx_lock = ctx.lock().unwrap();
+ if ctx_lock.started { return };
+ ctx_lock.started = true;
+ drop(ctx_lock);
+
+ 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;
+ chest.close().await;
+
+ let crafting_table = bot.open_crafting_table(&bot.world.find_one_block(|b| b.id == "minecraft:crafting_table")).await.unwrap();
+ bot.craft(&crafting_table, &bot.recipe_for("minecraft:sticks")).await?;
+ let pickaxe = bot.craft(&crafting_table, &bot.recipe_for("minecraft:wooden_pickaxe")).await?;
+ crafting_table.close().await;
+
+ bot.hold(&pickaxe);
+ loop {
+ if let Err(e) = bot.dig(bot.entity.feet_pos().down(1)).await {
+ println!("{:?}", e);
+ break;
+ }
+ }
+ }
+ },
+ _ => {}
+ }
+} \ No newline at end of file
diff --git a/azalea/examples/echo.rs b/azalea/examples/echo.rs
new file mode 100644
index 00000000..c9e46a09
--- /dev/null
+++ b/azalea/examples/echo.rs
@@ -0,0 +1,38 @@
+use azalea::{Account, Event};
+
+let account = Account::offline("bot");
+// or let account = azalea::Account::microsoft("access token").await;
+
+let bot = account.join("localhost".try_into().unwrap()).await.unwrap();
+
+loop {
+ match bot.next().await {
+ Event::Message(m) {
+ if m.username == bot.username { return };
+ bot.chat(m.message).await;
+ },
+ Event::Kicked(m) {
+ println!(m);
+ bot.reconnect().await.unwrap();
+ },
+ Event::Hunger(h) {
+ if !h.using_held_item() && h.hunger <= 17 {
+ match bot.hold(azalea::ItemGroup::Food).await {
+ Ok(_) => {},
+ Err(e) => {
+ println!("{}", e);
+ break;
+ }
+ }
+ match bot.use_held_item().await {
+ Ok(_) => {},
+ Err(e) => {
+ println!("{}", e);
+ break;
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+}
diff --git a/azalea/examples/mine_a_chunk.rs b/azalea/examples/mine_a_chunk.rs
new file mode 100644
index 00000000..6549f2b2
--- /dev/null
+++ b/azalea/examples/mine_a_chunk.rs
@@ -0,0 +1,27 @@
+use azalea::{Account, Accounts, Event, pathfinder};
+
+// You can use the `azalea::Bots` struct to control many bots as one unit.
+
+#[tokio::main]
+async fn main() {
+ let accounts = Accounts::new();
+
+ for i in 0..10 {
+ accounts.add(Account::offline(format!("bot{}", i)));
+ }
+
+ let bots = accounts.join("localhost".try_into().unwrap()).await.unwrap();
+
+ 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::BlockPos::new(0, 0, 0),
+ azalea::BlockPos::new(16, 255, 16)
+ ),
+ azalea::block::Air
+ ).await;
+}
diff --git a/azalea/examples/potatobot/README.md b/azalea/examples/potatobot/README.md
new file mode 100644
index 00000000..e494316e
--- /dev/null
+++ b/azalea/examples/potatobot/README.md
@@ -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
diff --git a/azalea/examples/potatobot/autoeat.rs b/azalea/examples/potatobot/autoeat.rs
new file mode 100644
index 00000000..44702295
--- /dev/null
+++ b/azalea/examples/potatobot/autoeat.rs
@@ -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;
+ }
+ }
+ }
+ _ => {}
+ }
+}
diff --git a/azalea/examples/potatobot/main.rs b/azalea/examples/potatobot/main.rs
new file mode 100644
index 00000000..94ed0005
--- /dev/null
+++ b/azalea/examples/potatobot/main.rs
@@ -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(())
+}
diff --git a/azalea/examples/pvp.rs b/azalea/examples/pvp.rs
new file mode 100644
index 00000000..5febdd45
--- /dev/null
+++ b/azalea/examples/pvp.rs
@@ -0,0 +1,33 @@
+use azalea::{Account, Accounts, Event, pathfinder};
+
+#[tokio::main]
+async fn main() {
+ let accounts = Accounts::new();
+ for i in 0..10 {
+ accounts.add(Account::offline(format!("bot{}", i)));
+ }
+
+ let bots = accounts.join("localhost".try_into().unwrap()).await.unwrap();
+
+ match bots.next().await {
+ Event::Tick {
+ // choose an arbitrary player within render distance to target
+ if let Some(target) = bots.world.find_one_entity(|e| e.id == "minecraft:player") {
+ for bot in bots {
+ bot.tick_goto_goal(
+ pathfinder::Goals::Reach(target.bounding_box)
+ );
+ // if target.bounding_box.distance(bot.eyes) < bot.reach_distance() {
+ if bot.entity.can_reach(target.bounding_box) {
+ bot.swing();
+ }
+ if !h.using_held_item() && bot.state.lock().hunger <= 17 {
+ bot.hold(azalea::ItemGroup::Food);
+ tokio::task::spawn(bot.use_held_item());
+ }
+ }
+ }
+ },
+ _ => {}
+ }
+}