testbot/commands/
movement.rs

1use std::time::Duration;
2
3use azalea::{
4    BlockPos, SprintDirection, WalkDirection,
5    brigadier::prelude::*,
6    entity::Position,
7    pathfinder::goals::{BlockPosGoal, RadiusGoal, XZGoal},
8    prelude::*,
9};
10use azalea_entity::dimensions::EntityDimensions;
11use parking_lot::Mutex;
12
13use super::{CommandSource, Ctx};
14use crate::BotTask;
15
16pub 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
33                    .bot
34                    .start_goto(BlockPosGoal(BlockPos::from(position)));
35                1
36            })
37            .then(literal("xz").then(argument("x", integer()).then(
38                argument("z", integer()).executes(|ctx: &Ctx| {
39                    let source = ctx.source.lock();
40                    let x = get_integer(ctx, "x").unwrap();
41                    let z = get_integer(ctx, "z").unwrap();
42                    println!("goto xz {x} {z}");
43                    source.reply("ok");
44                    source.bot.start_goto(XZGoal { x, z });
45                    1
46                }),
47            )))
48            .then(literal("radius").then(argument("radius", float()).then(
49                argument("x", integer()).then(argument("y", integer()).then(
50                    argument("z", integer()).executes(|ctx: &Ctx| {
51                        let source = ctx.source.lock();
52                        let radius = get_float(ctx, "radius").unwrap();
53                        let x = get_integer(ctx, "x").unwrap();
54                        let y = get_integer(ctx, "y").unwrap();
55                        let z = get_integer(ctx, "z").unwrap();
56                        println!("goto radius {radius}, position: {x} {y} {z}");
57                        source.reply("ok");
58                        source.bot.start_goto(RadiusGoal {
59                            pos: BlockPos::new(x, y, z).center(),
60                            radius,
61                        });
62                        1
63                    }),
64                )),
65            )))
66            .then(argument("x", integer()).then(argument("y", integer()).then(
67                argument("z", integer()).executes(|ctx: &Ctx| {
68                    let source = ctx.source.lock();
69                    let x = get_integer(ctx, "x").unwrap();
70                    let y = get_integer(ctx, "y").unwrap();
71                    let z = get_integer(ctx, "z").unwrap();
72                    println!("goto xyz {x} {y} {z}");
73                    source.reply("ok");
74                    source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75                    1
76                }),
77            ))),
78    );
79
80    commands.register(literal("down").executes(|ctx: &Ctx| {
81        let source = ctx.source.clone();
82        tokio::spawn(async move {
83            let bot = source.lock().bot.clone();
84            let position = BlockPos::from(bot.position());
85            source.lock().reply("mining...");
86            bot.mine(position.down(1)).await;
87            source.lock().reply("done");
88        });
89        1
90    }));
91
92    commands.register(
93        literal("look")
94            .executes(|ctx: &Ctx| {
95                // look for the sender
96                let mut source = ctx.source.lock();
97                let Some(entity) = source.entity() else {
98                    source.reply("I can't see you!");
99                    return 0;
100                };
101                let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102                    source.reply("I can't see you!");
103                    return 0;
104                };
105                let eye_height = source
106                    .bot
107                    .get_entity_component::<EntityDimensions>(entity)
108                    .map(|h| h.eye_height)
109                    .unwrap_or_default();
110                source.bot.look_at(position.up(eye_height as f64));
111                1
112            })
113            .then(argument("x", integer()).then(argument("y", integer()).then(
114                argument("z", integer()).executes(|ctx: &Ctx| {
115                    let pos = BlockPos::new(
116                        get_integer(ctx, "x").unwrap(),
117                        get_integer(ctx, "y").unwrap(),
118                        get_integer(ctx, "z").unwrap(),
119                    );
120                    println!("{pos:?}");
121                    let source = ctx.source.lock();
122                    source.bot.look_at(pos.center());
123                    1
124                }),
125            ))),
126    );
127
128    commands.register(
129        literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130            let mut seconds = get_float(ctx, "seconds").unwrap();
131            let source = ctx.source.lock();
132            let bot = source.bot.clone();
133
134            if seconds < 0. {
135                bot.walk(WalkDirection::Backward);
136                seconds = -seconds;
137            } else {
138                bot.walk(WalkDirection::Forward);
139            }
140
141            tokio::spawn(async move {
142                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143                bot.walk(WalkDirection::None);
144            });
145            source.reply(format!("ok, walking for {seconds} seconds"));
146            1
147        })),
148    );
149    commands.register(
150        literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151            let seconds = get_float(ctx, "seconds").unwrap();
152            let source = ctx.source.lock();
153            let bot = source.bot.clone();
154            bot.sprint(SprintDirection::Forward);
155            tokio::spawn(async move {
156                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157                bot.walk(WalkDirection::None);
158            });
159            source.reply(format!("ok, sprinting for {seconds} seconds"));
160            1
161        })),
162    );
163
164    commands.register(literal("north").executes(|ctx: &Ctx| {
165        let source = ctx.source.lock();
166        source.bot.set_direction(180., 0.);
167        source.reply("ok");
168        1
169    }));
170    commands.register(literal("south").executes(|ctx: &Ctx| {
171        let source = ctx.source.lock();
172        source.bot.set_direction(0., 0.);
173        source.reply("ok");
174        1
175    }));
176    commands.register(literal("east").executes(|ctx: &Ctx| {
177        let source = ctx.source.lock();
178        source.bot.set_direction(-90., 0.);
179        source.reply("ok");
180        1
181    }));
182    commands.register(literal("west").executes(|ctx: &Ctx| {
183        let source = ctx.source.lock();
184        source.bot.set_direction(90., 0.);
185        source.reply("ok");
186        1
187    }));
188    commands.register(
189        literal("jump")
190            .executes(|ctx: &Ctx| {
191                let source = ctx.source.lock();
192                source.bot.jump();
193                source.reply("ok");
194                1
195            })
196            .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197                let jumping = get_bool(ctx, "enabled").unwrap();
198                let source = ctx.source.lock();
199                source.bot.set_jumping(jumping);
200                1
201            })),
202    );
203
204    let sneak = |ctx: &Ctx| {
205        let source = ctx.source.lock();
206        source.bot.set_crouching(!source.bot.crouching());
207        source.reply("ok");
208        1
209    };
210    let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211        let sneaking = get_bool(ctx, "enabled").unwrap();
212        let source = ctx.source.lock();
213        source.bot.set_crouching(sneaking);
214        1
215    });
216    commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217    commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219    commands.register(literal("stop").executes(|ctx: &Ctx| {
220        let source = ctx.source.lock();
221        source.bot.stop_pathfinding();
222        source.reply("ok");
223        *source.state.task.lock() = BotTask::None;
224        1
225    }));
226}