Skip to content

Commit 0aa439d

Browse files
committed
rewrite testbot to use brigadier
1 parent 5ea1271 commit 0aa439d

File tree

8 files changed

+615
-425
lines changed

8 files changed

+615
-425
lines changed

azalea/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async fn main() {
5353
5454
ClientBuilder::new()
5555
.set_handler(handle)
56-
.start(account.clone(), "localhost")
56+
.start(account, "localhost")
5757
.await
5858
.unwrap();
5959
}

azalea/examples/testbot.rs

Lines changed: 0 additions & 424 deletions
This file was deleted.

azalea/examples/testbot/commands.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
pub mod combat;
2+
pub mod debug;
3+
pub mod movement;
4+
5+
use azalea::brigadier::prelude::*;
6+
use azalea::chat::ChatPacket;
7+
use azalea::ecs::prelude::Entity;
8+
use azalea::ecs::prelude::*;
9+
use azalea::entity::metadata::Player;
10+
use azalea::Client;
11+
use azalea::GameProfileComponent;
12+
use parking_lot::Mutex;
13+
14+
use crate::State;
15+
16+
pub type Ctx = CommandContext<Mutex<CommandSource>>;
17+
18+
pub struct CommandSource {
19+
pub bot: Client,
20+
pub state: State,
21+
pub chat: ChatPacket,
22+
}
23+
24+
impl CommandSource {
25+
pub fn reply(&self, message: &str) {
26+
if self.chat.is_whisper() {
27+
self.bot
28+
.chat(&format!("/w {} {}", self.chat.username().unwrap(), message));
29+
} else {
30+
self.bot.chat(message);
31+
}
32+
}
33+
34+
pub fn entity(&mut self) -> Option<Entity> {
35+
let username = self.chat.username()?;
36+
self.bot.entity_by::<With<Player>, &GameProfileComponent>(
37+
|profile: &&GameProfileComponent| profile.name == username,
38+
)
39+
}
40+
}
41+
42+
pub fn register_commands(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
43+
combat::register(commands);
44+
debug::register(commands);
45+
movement::register(commands);
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use azalea::brigadier::prelude::*;
2+
use parking_lot::Mutex;
3+
4+
use super::{CommandSource, Ctx};
5+
6+
pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
7+
commands.register(
8+
literal("killaura").then(argument("enabled", bool()).executes(|ctx: &Ctx| {
9+
let enabled = get_bool(ctx, "enabled").unwrap();
10+
let source = ctx.source.lock();
11+
let bot = source.bot.clone();
12+
{
13+
let mut ecs = bot.ecs.lock();
14+
let mut entity = ecs.entity_mut(bot.entity);
15+
let mut state = entity.get_mut::<crate::State>().unwrap();
16+
state.killaura = enabled
17+
}
18+
source.reply(if enabled {
19+
"Enabled killaura"
20+
} else {
21+
"Disabled killaura"
22+
});
23+
1
24+
})),
25+
);
26+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! Commands for debugging and getting the current state of the bot.
2+
3+
use azalea::{
4+
brigadier::prelude::*,
5+
entity::{LookDirection, Position},
6+
interact::HitResultComponent,
7+
world::MinecraftEntityId,
8+
};
9+
use parking_lot::Mutex;
10+
11+
use super::{CommandSource, Ctx};
12+
13+
pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
14+
commands.register(literal("ping").executes(|ctx: &Ctx| {
15+
let source = ctx.source.lock();
16+
source.reply("pong!");
17+
1
18+
}));
19+
20+
commands.register(literal("whereami").executes(|ctx: &Ctx| {
21+
let mut source = ctx.source.lock();
22+
let Some(entity) = source.entity() else {
23+
source.reply("You aren't in render distance!");
24+
return 0;
25+
};
26+
let position = source.bot.entity_component::<Position>(entity);
27+
source.reply(&format!(
28+
"You are at {}, {}, {}",
29+
position.x, position.y, position.z
30+
));
31+
1
32+
}));
33+
34+
commands.register(literal("entityid").executes(|ctx: &Ctx| {
35+
let mut source = ctx.source.lock();
36+
let Some(entity) = source.entity() else {
37+
source.reply("You aren't in render distance!");
38+
return 0;
39+
};
40+
let entity_id = source.bot.entity_component::<MinecraftEntityId>(entity);
41+
source.reply(&format!(
42+
"Your Minecraft ID is {} and your ECS id is {entity:?}",
43+
*entity_id
44+
));
45+
1
46+
}));
47+
48+
let whereareyou = |ctx: &Ctx| {
49+
let source = ctx.source.lock();
50+
let position = source.bot.position();
51+
source.reply(&format!(
52+
"I'm at {}, {}, {}",
53+
position.x, position.y, position.z
54+
));
55+
1
56+
};
57+
commands.register(literal("whereareyou").executes(whereareyou));
58+
commands.register(literal("pos").executes(whereareyou));
59+
60+
commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
61+
let source = ctx.source.lock();
62+
source.reply(&format!(
63+
"I am {} ({})",
64+
source.bot.username(),
65+
source.bot.uuid()
66+
));
67+
1
68+
}));
69+
70+
commands.register(literal("getdirection").executes(|ctx: &Ctx| {
71+
let source = ctx.source.lock();
72+
let direction = source.bot.component::<LookDirection>();
73+
source.reply(&format!(
74+
"I'm looking at {}, {}",
75+
direction.y_rot, direction.x_rot
76+
));
77+
1
78+
}));
79+
80+
commands.register(literal("health").executes(|ctx: &Ctx| {
81+
let source = ctx.source.lock();
82+
83+
let health = source.bot.health();
84+
source.reply(&format!("I have {health} health"));
85+
1
86+
}));
87+
88+
commands.register(literal("lookingat").executes(|ctx: &Ctx| {
89+
let source = ctx.source.lock();
90+
91+
let hit_result = *source.bot.component::<HitResultComponent>();
92+
93+
if hit_result.miss {
94+
source.reply("I'm not looking at anything");
95+
return 1;
96+
}
97+
98+
let block_pos = hit_result.block_pos;
99+
let block = source.bot.world().read().get_block_state(&block_pos);
100+
101+
source.reply(&format!("I'm looking at {block:?} at {block_pos:?}"));
102+
103+
1
104+
}));
105+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use std::time::Duration;
2+
3+
use azalea::{
4+
brigadier::prelude::*,
5+
entity::{EyeHeight, Position},
6+
pathfinder::goals::{BlockPosGoal, XZGoal},
7+
prelude::*,
8+
BlockPos, SprintDirection, WalkDirection,
9+
};
10+
use parking_lot::Mutex;
11+
12+
use crate::BotTask;
13+
14+
use super::{CommandSource, Ctx};
15+
16+
pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17+
commands.register(
18+
literal("goto")
19+
.executes(|ctx: &Ctx| {
20+
let mut source = ctx.source.lock();
21+
println!("got goto");
22+
// look for the sender
23+
let Some(entity) = source.entity() else {
24+
source.reply("I can't see you!");
25+
return 0;
26+
};
27+
let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28+
source.reply("I can't see you!");
29+
return 0;
30+
};
31+
source.reply("ok");
32+
source.bot.goto(BlockPosGoal(BlockPos::from(position)));
33+
1
34+
})
35+
.then(literal("xz").then(argument("x", integer()).then(
36+
argument("z", integer()).executes(|ctx: &Ctx| {
37+
let source = ctx.source.lock();
38+
let x = get_integer(ctx, "x").unwrap();
39+
let z = get_integer(ctx, "z").unwrap();
40+
println!("goto xz {x} {z}");
41+
source.reply("ok");
42+
source.bot.goto(XZGoal { x, z });
43+
1
44+
}),
45+
)))
46+
.then(argument("x", integer()).then(argument("y", integer()).then(
47+
argument("z", integer()).executes(|ctx: &Ctx| {
48+
let source = ctx.source.lock();
49+
let x = get_integer(ctx, "x").unwrap();
50+
let y = get_integer(ctx, "y").unwrap();
51+
let z = get_integer(ctx, "z").unwrap();
52+
println!("goto xyz {x} {y} {z}");
53+
source.reply("ok");
54+
source.bot.goto(BlockPosGoal(BlockPos::new(x, y, z)));
55+
1
56+
}),
57+
))),
58+
);
59+
60+
commands.register(literal("down").executes(|ctx: &Ctx| {
61+
let source = ctx.source.clone();
62+
tokio::spawn(async move {
63+
let mut bot = source.lock().bot.clone();
64+
let position = BlockPos::from(bot.position());
65+
source.lock().reply("mining...");
66+
bot.mine(position.down(1)).await;
67+
source.lock().reply("done");
68+
});
69+
1
70+
}));
71+
72+
commands.register(
73+
literal("look")
74+
.executes(|ctx: &Ctx| {
75+
// look for the sender
76+
let mut source = ctx.source.lock();
77+
let Some(entity) = source.entity() else {
78+
source.reply("I can't see you!");
79+
return 0;
80+
};
81+
let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
82+
source.reply("I can't see you!");
83+
return 0;
84+
};
85+
let eye_height = source
86+
.bot
87+
.get_entity_component::<EyeHeight>(entity)
88+
.map(|h| *h)
89+
.unwrap_or_default();
90+
source.bot.look_at(position.up(eye_height as f64));
91+
1
92+
})
93+
.then(argument("x", integer()).then(argument("y", integer()).then(
94+
argument("z", integer()).executes(|ctx: &Ctx| {
95+
let pos = BlockPos::new(
96+
get_integer(ctx, "x").unwrap(),
97+
get_integer(ctx, "y").unwrap(),
98+
get_integer(ctx, "z").unwrap(),
99+
);
100+
println!("{:?}", pos);
101+
let mut source = ctx.source.lock();
102+
source.bot.look_at(pos.center());
103+
1
104+
}),
105+
))),
106+
);
107+
108+
commands.register(
109+
literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
110+
let mut seconds = get_float(ctx, "seconds").unwrap();
111+
let source = ctx.source.lock();
112+
let mut bot = source.bot.clone();
113+
114+
if seconds < 0. {
115+
bot.walk(WalkDirection::Backward);
116+
seconds = -seconds;
117+
} else {
118+
bot.walk(WalkDirection::Forward);
119+
}
120+
121+
tokio::spawn(async move {
122+
tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
123+
bot.walk(WalkDirection::None);
124+
});
125+
source.reply(&format!("ok, walking for {seconds} seconds"));
126+
1
127+
})),
128+
);
129+
commands.register(
130+
literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
131+
let seconds = get_float(ctx, "seconds").unwrap();
132+
let source = ctx.source.lock();
133+
let mut bot = source.bot.clone();
134+
bot.sprint(SprintDirection::Forward);
135+
tokio::spawn(async move {
136+
tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
137+
bot.walk(WalkDirection::None);
138+
});
139+
source.reply(&format!("ok, spriting for {seconds} seconds"));
140+
1
141+
})),
142+
);
143+
144+
commands.register(literal("north").executes(|ctx: &Ctx| {
145+
let mut source = ctx.source.lock();
146+
source.bot.set_direction(180., 0.);
147+
source.reply("ok");
148+
1
149+
}));
150+
commands.register(literal("south").executes(|ctx: &Ctx| {
151+
let mut source = ctx.source.lock();
152+
source.bot.set_direction(0., 0.);
153+
source.reply("ok");
154+
1
155+
}));
156+
commands.register(literal("east").executes(|ctx: &Ctx| {
157+
let mut source = ctx.source.lock();
158+
source.bot.set_direction(-90., 0.);
159+
source.reply("ok");
160+
1
161+
}));
162+
commands.register(literal("west").executes(|ctx: &Ctx| {
163+
let mut source = ctx.source.lock();
164+
source.bot.set_direction(90., 0.);
165+
source.reply("ok");
166+
1
167+
}));
168+
commands.register(
169+
literal("jump")
170+
.executes(|ctx: &Ctx| {
171+
let mut source = ctx.source.lock();
172+
source.bot.jump();
173+
source.reply("ok");
174+
1
175+
})
176+
.then(argument("enabled", bool()).executes(|ctx: &Ctx| {
177+
let jumping = get_bool(ctx, "enabled").unwrap();
178+
let mut source = ctx.source.lock();
179+
source.bot.set_jumping(jumping);
180+
1
181+
})),
182+
);
183+
184+
commands.register(literal("stop").executes(|ctx: &Ctx| {
185+
let source = ctx.source.lock();
186+
source.bot.stop_pathfinding();
187+
source.reply("ok");
188+
*source.state.task.lock() = BotTask::None;
189+
1
190+
}));
191+
}

0 commit comments

Comments
 (0)