aboutsummaryrefslogtreecommitdiff
path: root/azalea/examples
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2026-05-06 18:38:23 -0545
committermat <git@matdoes.dev>2026-05-07 08:05:58 -1200
commitcabc8b60a729ba17f5b75f7a7956c6d1ddcc8919 (patch)
tree237fd12a9768fe7431ce42dfbdde60f4c7850e06 /azalea/examples
parent9ffd0e80bbb3feace231553d6539124585b03e3c (diff)
downloadazalea-drasl-cabc8b60a729ba17f5b75f7a7956c6d1ddcc8919.tar.xz
azalea-brigadier now allows commands to return a Result
Diffstat (limited to 'azalea/examples')
-rw-r--r--azalea/examples/steal.rs6
-rw-r--r--azalea/examples/testbot/commands.rs5
-rw-r--r--azalea/examples/testbot/commands/combat.rs11
-rw-r--r--azalea/examples/testbot/commands/debug.rs110
-rw-r--r--azalea/examples/testbot/commands/movement.rs74
-rw-r--r--azalea/examples/testbot/main.rs17
6 files changed, 116 insertions, 107 deletions
diff --git a/azalea/examples/steal.rs b/azalea/examples/steal.rs
index d5c0b913..44d2eb57 100644
--- a/azalea/examples/steal.rs
+++ b/azalea/examples/steal.rs
@@ -53,9 +53,9 @@ async fn steal(bot: Client, state: State) -> eyre::Result<()> {
loop {
let chest_block = bot
- .world()
+ .world()?
.read()
- .find_blocks(bot.position(), &BlockKind::Chest.into())
+ .find_blocks(bot.position()?, &BlockKind::Chest.into())
.find(
// find the closest chest that hasn't been checked
|block_pos| !state.checked_chests.lock().contains(block_pos),
@@ -68,7 +68,7 @@ async fn steal(bot: Client, state: State) -> eyre::Result<()> {
bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
- let Some(chest) = bot.open_container_at(chest_block).await else {
+ let Some(chest) = bot.open_container_at(chest_block).await? else {
println!("Couldn't open chest at {chest_block:?}");
continue;
};
diff --git a/azalea/examples/testbot/commands.rs b/azalea/examples/testbot/commands.rs
index 03705617..599c693d 100644
--- a/azalea/examples/testbot/commands.rs
+++ b/azalea/examples/testbot/commands.rs
@@ -11,7 +11,8 @@ use parking_lot::Mutex;
use crate::State;
-pub type Ctx = CommandContext<Mutex<CommandSource>>;
+pub type Ctx = CommandContext<Mutex<CommandSource>, eyre::Result<i32>>;
+pub type Dispatcher = CommandDispatcher<Mutex<CommandSource>, eyre::Result<i32>>;
pub struct CommandSource {
pub bot: Client,
@@ -42,7 +43,7 @@ impl CommandSource {
}
}
-pub fn register_commands(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register_commands(commands: &mut Dispatcher) {
combat::register(commands);
debug::register(commands);
movement::register(commands);
diff --git a/azalea/examples/testbot/commands/combat.rs b/azalea/examples/testbot/commands/combat.rs
index af147e96..617ac1bd 100644
--- a/azalea/examples/testbot/commands/combat.rs
+++ b/azalea/examples/testbot/commands/combat.rs
@@ -1,22 +1,21 @@
use azalea::brigadier::prelude::*;
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
-use crate::State;
+use super::Ctx;
+use crate::{State, commands::Dispatcher};
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(
literal("killaura").then(argument("enabled", bool()).executes(|ctx: &Ctx| {
let enabled = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
let bot = source.bot.clone();
- bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled);
+ bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled)?;
source.reply(if enabled {
"Enabled killaura"
} else {
"Disabled killaura"
});
- 1
+ Ok(1)
})),
);
}
diff --git a/azalea/examples/testbot/commands/debug.rs b/azalea/examples/testbot/commands/debug.rs
index 55b41403..50821a8b 100644
--- a/azalea/examples/testbot/commands/debug.rs
+++ b/azalea/examples/testbot/commands/debug.rs
@@ -14,72 +14,72 @@ use azalea::{
},
};
use azalea_core::hit_result::HitResult;
-use azalea_entity::{EntityKindComponent, metadata};
+use azalea_entity::metadata;
use azalea_inventory::{Menu, components::MaxStackSize};
use azalea_world::{Worlds, chunk::storage::WeakChunkStorage};
use bevy_app::AppExit;
use bevy_ecs::{message::Messages, query::With, world::EntityRef};
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
+use super::Ctx;
+use crate::commands::Dispatcher;
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(literal("ping").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.reply("pong!");
- 1
+ Ok(1)
}));
commands.register(
literal("say").then(argument("message", greedy_string()).executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let message = get_string(ctx, "message").unwrap();
source.bot.chat(message);
- 1
+ Ok(1)
})),
);
commands.register(literal("disconnect").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.disconnect();
- 1
+ Ok(1)
}));
commands.register(literal("whereami").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
source.reply(format!(
"You are at {}, {}, {}",
position.x, position.y, position.z
));
- 1
+ Ok(1)
}));
commands.register(literal("entityid").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let entity_id = entity.minecraft_id();
+ let entity_id = entity.minecraft_id()?;
source.reply(format!(
"Your Minecraft ID is {} and your ECS ID is {entity:?}",
*entity_id
));
- 1
+ Ok(1)
}));
let whereareyou = |ctx: &Ctx| {
let source = ctx.source.lock();
- let position = source.bot.position();
+ let position = source.bot.position()?;
source.reply(format!(
"I'm at {}, {}, {}",
position.x, position.y, position.z
));
- 1
+ Ok(1)
};
commands.register(literal("whereareyou").executes(whereareyou));
commands.register(literal("pos").executes(whereareyou));
@@ -92,45 +92,45 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.uuid(),
source.bot.entity
));
- 1
+ Ok(1)
}));
commands.register(literal("getdirection").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let direction = source.bot.direction();
+ let direction = source.bot.direction()?;
source.reply(format!(
"I'm looking at {}, {}",
direction.y_rot(),
direction.x_rot()
));
- 1
+ Ok(1)
}));
commands.register(literal("health").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let health = source.bot.health();
+ let health = source.bot.health()?;
source.reply(format!("I have {health} health"));
- 1
+ Ok(1)
}));
commands.register(literal("lookingat").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let hit_result = source.bot.hit_result();
+ let hit_result = source.bot.hit_result()?;
match &hit_result {
HitResult::Block(r) => {
if r.miss {
source.reply("I'm not looking at anything");
- return 0;
+ return Ok(0);
}
let block_pos = r.block_pos;
- let block = source.bot.world().read().get_block_state(block_pos);
+ let block = source.bot.world()?.read().get_block_state(block_pos);
source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
}
HitResult::Entity(r) => {
- let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
+ let entity_kind = source.bot.entity_ref_for(r.entity).kind()?;
source.reply(format!(
"I'm looking at {entity_kind} ({:?}) at {}",
r.entity, r.location
@@ -138,7 +138,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
- 1
+ Ok(1)
}));
commands.register(literal("getblock").then(argument("x", integer()).then(
@@ -149,9 +149,9 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let z = get_integer(ctx, "z").unwrap();
println!("getblock xyz {x} {y} {z}");
let block_pos = BlockPos::new(x, y, z);
- let block = source.bot.world().read().get_block_state(block_pos);
+ let block = source.bot.world()?.read().get_block_state(block_pos);
source.reply(format!("BlockKind at {block_pos} is {block:?}"));
- 1
+ Ok(1)
})),
)));
commands.register(literal("getfluid").then(argument("x", integer()).then(
@@ -162,15 +162,15 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let z = get_integer(ctx, "z").unwrap();
println!("getfluid xyz {x} {y} {z}");
let block_pos = BlockPos::new(x, y, z);
- let block = source.bot.world().read().get_fluid_state(block_pos);
+ let block = source.bot.world()?.read().get_fluid_state(block_pos);
source.reply(format!("Fluid at {block_pos} is {block:?}"));
- 1
+ Ok(1)
})),
)));
commands.register(literal("inventory").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- for item in source.bot.menu().slots() {
+ for item in source.bot.menu()?.slots() {
if item.is_empty() {
continue;
}
@@ -181,25 +181,25 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
}
- 1
+ Ok(1)
}));
commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let pathfinder = source.bot.get_component::<Pathfinder>();
- let Some(pathfinder) = pathfinder else {
+ let pathfinder = source.bot.component::<Pathfinder>();
+ let Ok(pathfinder) = pathfinder else {
source.reply("I don't have the Pathfinder component");
- return 1;
+ return Ok(1);
};
source.reply(format!(
"pathfinder.is_calculating: {}",
pathfinder.is_calculating
));
- let executing_path = source.bot.get_component::<ExecutingPath>();
- let Some(executing_path) = executing_path else {
+ let executing_path = source.bot.component::<ExecutingPath>();
+ let Ok(executing_path) = executing_path else {
source.reply("I'm not executing a path");
- return 1;
+ return Ok(1);
};
source.reply(format!(
"is_path_partial: {}, path.len: {}, queued_path.len: {}",
@@ -211,20 +211,20 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
"n/a".to_owned()
},
));
- 1
+ Ok(1)
}));
commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
let position = BlockPos::from(position);
let mut edges = Vec::new();
- let cached_world = CachedWorld::new(source.bot.world(), position);
+ let cached_world = CachedWorld::new(source.bot.world()?, position);
let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
let custom_state = CustomPathfinderStateRef::default();
@@ -247,41 +247,41 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
- 1
+ Ok(1)
}));
commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.start_use_item();
source.reply("Ok!");
- 1
+ Ok(1)
}));
commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let max_stack_size = source
.bot
- .get_held_item()
+ .get_held_item()?
.get_component::<MaxStackSize>()
.map_or(-1, |s| s.count);
source.reply(format!("{max_stack_size}"));
- 1
+ Ok(1)
}));
commands.register(literal("dimensions").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let bot_dimensions = source.bot.dimensions();
source.reply(format!("{bot_dimensions:?}"));
- 1
+ Ok(1)
}));
commands.register(literal("players").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let player_entities = source
.bot
- .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
- let tab_list = source.bot.tab_list();
+ .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true)?;
+ let tab_list = source.bot.tab_list()?;
for player_entity in player_entities {
- let uuid = player_entity.uuid();
+ let uuid = player_entity.uuid()?;
source.reply(format!(
"{} - {} ({:?})",
player_entity.id(),
@@ -289,7 +289,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
uuid
));
}
- 1
+ Ok(1)
}));
commands.register(literal("enchants").executes(|ctx: &Ctx| {
@@ -297,15 +297,15 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.with_registry_holder(|r| {
let enchants = &r.enchantment;
println!("enchants: {enchants:?}");
- });
- 1
+ })?;
+ Ok(1)
}));
commands.register(literal("attributes").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let attributes = source.bot.attributes();
println!("attributes: {attributes:?}");
- 1
+ Ok(1)
}));
commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
@@ -406,7 +406,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
});
- 1
+ Ok(1)
}));
commands.register(literal("exit").executes(|ctx: &Ctx| {
@@ -427,6 +427,6 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
.write_message(AppExit::Success);
});
- 1
+ Ok(1)
}));
}
diff --git a/azalea/examples/testbot/commands/movement.rs b/azalea/examples/testbot/commands/movement.rs
index 500e17b0..df3b7418 100644
--- a/azalea/examples/testbot/commands/movement.rs
+++ b/azalea/examples/testbot/commands/movement.rs
@@ -6,11 +6,11 @@ use azalea::{
pathfinder::goals::{BlockPosGoal, RadiusGoal, XZGoal},
prelude::*,
};
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
+use super::Ctx;
+use crate::commands::Dispatcher;
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(
literal("goto")
.executes(|ctx: &Ctx| {
@@ -19,14 +19,14 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
// look for the sender
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
source.reply("ok");
source
.bot
.start_goto(BlockPosGoal(BlockPos::from(position.up(0.5))));
- 1
+ Ok(1)
})
.then(literal("xz").then(argument("x", integer()).then(
argument("z", integer()).executes(|ctx: &Ctx| {
@@ -36,7 +36,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("goto xz {x} {z}");
source.reply("ok");
source.bot.start_goto(XZGoal { x, z });
- 1
+ Ok(1)
}),
)))
.then(literal("radius").then(argument("radius", float()).then(
@@ -53,7 +53,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
pos: BlockPos::new(x, y, z).center(),
radius,
});
- 1
+ Ok(1)
}),
)),
)))
@@ -66,7 +66,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("goto xyz {x} {y} {z}");
source.reply("ok");
source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
- 1
+ Ok(1)
}),
))),
);
@@ -77,23 +77,23 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
// look for the sender
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
source.reply("ok");
*source.state.following_entity.lock() = Some(entity);
- 1
+ Ok(1)
}));
commands.register(literal("down").executes(|ctx: &Ctx| {
let source = ctx.source.clone();
+ let bot = source.lock().bot.clone();
+ let position = BlockPos::from(bot.position()?);
tokio::spawn(async move {
- let bot = source.lock().bot.clone();
- let position = BlockPos::from(bot.position());
source.lock().reply("mining...");
bot.mine(position.down(1)).await;
source.lock().reply("done");
});
- 1
+ Ok(1)
}));
commands.register(
@@ -103,11 +103,11 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
- let eye_position = entity.eye_position();
+ let eye_position = entity.eye_position()?;
source.bot.look_at(eye_position);
- 1
+ Ok(1)
})
.then(argument("x", integer()).then(argument("y", integer()).then(
argument("z", integer()).executes(|ctx: &Ctx| {
@@ -119,7 +119,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("{pos:?}");
let source = ctx.source.lock();
source.bot.look_at(pos.center());
- 1
+ Ok(1)
}),
))),
);
@@ -142,7 +142,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
bot.walk(WalkDirection::None);
});
source.reply(format!("ok, walking for {seconds} seconds"));
- 1
+ Ok(1)
})),
);
commands.register(
@@ -156,33 +156,33 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
bot.walk(WalkDirection::None);
});
source.reply(format!("ok, sprinting for {seconds} seconds"));
- 1
+ Ok(1)
})),
);
commands.register(literal("north").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(180., 0.);
+ source.bot.set_direction(180., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("south").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(0., 0.);
+ source.bot.set_direction(0., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("east").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(-90., 0.);
+ source.bot.set_direction(-90., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("west").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(90., 0.);
+ source.bot.set_direction(90., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(
literal("jump")
@@ -190,27 +190,27 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let source = ctx.source.lock();
source.bot.jump();
source.reply("ok");
- 1
+ Ok(1)
})
.then(argument("enabled", bool()).executes(|ctx: &Ctx| {
let jumping = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
- source.bot.set_jumping(jumping);
- 1
+ source.bot.set_jumping(jumping)?;
+ Ok(1)
})),
);
let sneak = |ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_crouching(!source.bot.crouching());
+ source.bot.set_crouching(!source.bot.crouching())?;
source.reply("ok");
- 1
+ Ok(1)
};
let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
let sneaking = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
- source.bot.set_crouching(sneaking);
- 1
+ source.bot.set_crouching(sneaking)?;
+ Ok(1)
});
commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
@@ -220,13 +220,13 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.stop_pathfinding();
source.reply("ok");
*source.state.following_entity.lock() = None;
- 1
+ Ok(1)
}));
commands.register(literal("forcestop").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.force_stop_pathfinding();
source.reply("ok");
*source.state.following_entity.lock() = None;
- 1
+ Ok(1)
}));
}
diff --git a/azalea/examples/testbot/main.rs b/azalea/examples/testbot/main.rs
index 806780b7..7778463c 100644
--- a/azalea/examples/testbot/main.rs
+++ b/azalea/examples/testbot/main.rs
@@ -126,7 +126,7 @@ impl State {
#[derive(Clone, Default, Resource)]
struct SwarmState {
pub args: Arc<Args>,
- pub commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
+ pub commands: Arc<commands::Dispatcher>,
}
async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result<()> {
@@ -137,7 +137,7 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
bot.set_client_information(ClientInformation {
view_distance: 32,
..Default::default()
- });
+ })?;
if swarm.args.pathfinder_debug_particles {
bot.ecs
.write()
@@ -169,7 +169,16 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
state: state.clone(),
}),
) {
- Ok(_) => {}
+ Ok(Ok(_)) => {}
+ Ok(Err(err)) => {
+ eprintln!("azalea error: {err:?}");
+ let command_source = CommandSource {
+ bot,
+ chat: chat.clone(),
+ state: state.clone(),
+ };
+ command_source.reply(format!("azalea error: {err:?}"));
+ }
Err(err) => {
eprintln!("{err:?}");
let command_source = CommandSource {
@@ -200,7 +209,7 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
.max_timeout(Duration::from_secs(1)),
);
} else {
- following.look_at();
+ following.look_at()?;
}
}
}