ArgumentBuilder

Struct ArgumentBuilder 

Source
pub struct ArgumentBuilder<S> { /* private fields */ }
Expand description

A node that hasn’t yet been built.

Implementations§

Source§

impl<S> ArgumentBuilder<S>

A node that isn’t yet built.

Source

pub fn new(value: ArgumentBuilderType<S>) -> Self

Source

pub fn then(self, argument: ArgumentBuilder<S>) -> Self

Continue building this node with a child node.

literal("foo").then(literal("bar").executes(|ctx: &CommandContext<()>| 42))
Examples found in repository?
azalea/examples/testbot/commands/combat.rs (lines 9-20)
7pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
8    commands.register(
9        literal("killaura").then(argument("enabled", bool()).executes(|ctx: &Ctx| {
10            let enabled = get_bool(ctx, "enabled").unwrap();
11            let source = ctx.source.lock();
12            let bot = source.bot.clone();
13            bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled);
14            source.reply(if enabled {
15                "Enabled killaura"
16            } else {
17                "Disabled killaura"
18            });
19            1
20        })),
21    );
22}
More examples
Hide additional examples
azalea/examples/testbot/commands/movement.rs (lines 32-42)
14pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
15    commands.register(
16        literal("goto")
17            .executes(|ctx: &Ctx| {
18                let mut source = ctx.source.lock();
19                println!("got goto");
20                // look for the sender
21                let Some(entity) = source.entity() else {
22                    source.reply("I can't see you!");
23                    return 0;
24                };
25                let position = entity.position();
26                source.reply("ok");
27                source
28                    .bot
29                    .start_goto(BlockPosGoal(BlockPos::from(position)));
30                1
31            })
32            .then(literal("xz").then(argument("x", integer()).then(
33                argument("z", integer()).executes(|ctx: &Ctx| {
34                    let source = ctx.source.lock();
35                    let x = get_integer(ctx, "x").unwrap();
36                    let z = get_integer(ctx, "z").unwrap();
37                    println!("goto xz {x} {z}");
38                    source.reply("ok");
39                    source.bot.start_goto(XZGoal { x, z });
40                    1
41                }),
42            )))
43            .then(literal("radius").then(argument("radius", float()).then(
44                argument("x", integer()).then(argument("y", integer()).then(
45                    argument("z", integer()).executes(|ctx: &Ctx| {
46                        let source = ctx.source.lock();
47                        let radius = get_float(ctx, "radius").unwrap();
48                        let x = get_integer(ctx, "x").unwrap();
49                        let y = get_integer(ctx, "y").unwrap();
50                        let z = get_integer(ctx, "z").unwrap();
51                        println!("goto radius {radius}, position: {x} {y} {z}");
52                        source.reply("ok");
53                        source.bot.start_goto(RadiusGoal {
54                            pos: BlockPos::new(x, y, z).center(),
55                            radius,
56                        });
57                        1
58                    }),
59                )),
60            )))
61            .then(argument("x", integer()).then(argument("y", integer()).then(
62                argument("z", integer()).executes(|ctx: &Ctx| {
63                    let source = ctx.source.lock();
64                    let x = get_integer(ctx, "x").unwrap();
65                    let y = get_integer(ctx, "y").unwrap();
66                    let z = get_integer(ctx, "z").unwrap();
67                    println!("goto xyz {x} {y} {z}");
68                    source.reply("ok");
69                    source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
70                    1
71                }),
72            ))),
73    );
74
75    commands.register(literal("down").executes(|ctx: &Ctx| {
76        let source = ctx.source.clone();
77        tokio::spawn(async move {
78            let bot = source.lock().bot.clone();
79            let position = BlockPos::from(bot.position());
80            source.lock().reply("mining...");
81            bot.mine(position.down(1)).await;
82            source.lock().reply("done");
83        });
84        1
85    }));
86
87    commands.register(
88        literal("look")
89            .executes(|ctx: &Ctx| {
90                // look for the sender
91                let mut source = ctx.source.lock();
92                let Some(entity) = source.entity() else {
93                    source.reply("I can't see you!");
94                    return 0;
95                };
96                let eye_position = entity.eye_position();
97                source.bot.look_at(eye_position);
98                1
99            })
100            .then(argument("x", integer()).then(argument("y", integer()).then(
101                argument("z", integer()).executes(|ctx: &Ctx| {
102                    let pos = BlockPos::new(
103                        get_integer(ctx, "x").unwrap(),
104                        get_integer(ctx, "y").unwrap(),
105                        get_integer(ctx, "z").unwrap(),
106                    );
107                    println!("{pos:?}");
108                    let source = ctx.source.lock();
109                    source.bot.look_at(pos.center());
110                    1
111                }),
112            ))),
113    );
114
115    commands.register(
116        literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
117            let mut seconds = get_float(ctx, "seconds").unwrap();
118            let source = ctx.source.lock();
119            let bot = source.bot.clone();
120
121            if seconds < 0. {
122                bot.walk(WalkDirection::Backward);
123                seconds = -seconds;
124            } else {
125                bot.walk(WalkDirection::Forward);
126            }
127
128            tokio::spawn(async move {
129                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
130                bot.walk(WalkDirection::None);
131            });
132            source.reply(format!("ok, walking for {seconds} seconds"));
133            1
134        })),
135    );
136    commands.register(
137        literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
138            let seconds = get_float(ctx, "seconds").unwrap();
139            let source = ctx.source.lock();
140            let bot = source.bot.clone();
141            bot.sprint(SprintDirection::Forward);
142            tokio::spawn(async move {
143                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
144                bot.walk(WalkDirection::None);
145            });
146            source.reply(format!("ok, sprinting for {seconds} seconds"));
147            1
148        })),
149    );
150
151    commands.register(literal("north").executes(|ctx: &Ctx| {
152        let source = ctx.source.lock();
153        source.bot.set_direction(180., 0.);
154        source.reply("ok");
155        1
156    }));
157    commands.register(literal("south").executes(|ctx: &Ctx| {
158        let source = ctx.source.lock();
159        source.bot.set_direction(0., 0.);
160        source.reply("ok");
161        1
162    }));
163    commands.register(literal("east").executes(|ctx: &Ctx| {
164        let source = ctx.source.lock();
165        source.bot.set_direction(-90., 0.);
166        source.reply("ok");
167        1
168    }));
169    commands.register(literal("west").executes(|ctx: &Ctx| {
170        let source = ctx.source.lock();
171        source.bot.set_direction(90., 0.);
172        source.reply("ok");
173        1
174    }));
175    commands.register(
176        literal("jump")
177            .executes(|ctx: &Ctx| {
178                let source = ctx.source.lock();
179                source.bot.jump();
180                source.reply("ok");
181                1
182            })
183            .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
184                let jumping = get_bool(ctx, "enabled").unwrap();
185                let source = ctx.source.lock();
186                source.bot.set_jumping(jumping);
187                1
188            })),
189    );
190
191    let sneak = |ctx: &Ctx| {
192        let source = ctx.source.lock();
193        source.bot.set_crouching(!source.bot.crouching());
194        source.reply("ok");
195        1
196    };
197    let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
198        let sneaking = get_bool(ctx, "enabled").unwrap();
199        let source = ctx.source.lock();
200        source.bot.set_crouching(sneaking);
201        1
202    });
203    commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
204    commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
205
206    commands.register(literal("stop").executes(|ctx: &Ctx| {
207        let source = ctx.source.lock();
208        source.bot.stop_pathfinding();
209        source.reply("ok");
210        *source.state.task.lock() = BotTask::None;
211        1
212    }));
213}
azalea/examples/testbot/commands/debug.rs (lines 132-144)
22pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
23    commands.register(literal("ping").executes(|ctx: &Ctx| {
24        let source = ctx.source.lock();
25        source.reply("pong!");
26        1
27    }));
28
29    commands.register(literal("disconnect").executes(|ctx: &Ctx| {
30        let source = ctx.source.lock();
31        source.bot.disconnect();
32        1
33    }));
34
35    commands.register(literal("whereami").executes(|ctx: &Ctx| {
36        let mut source = ctx.source.lock();
37        let Some(entity) = source.entity() else {
38            source.reply("You aren't in render distance!");
39            return 0;
40        };
41        let position = entity.position();
42        source.reply(format!(
43            "You are at {}, {}, {}",
44            position.x, position.y, position.z
45        ));
46        1
47    }));
48
49    commands.register(literal("entityid").executes(|ctx: &Ctx| {
50        let mut source = ctx.source.lock();
51        let Some(entity) = source.entity() else {
52            source.reply("You aren't in render distance!");
53            return 0;
54        };
55        let entity_id = entity.minecraft_id();
56        source.reply(format!(
57            "Your Minecraft ID is {} and your ECS ID is {entity:?}",
58            *entity_id
59        ));
60        1
61    }));
62
63    let whereareyou = |ctx: &Ctx| {
64        let source = ctx.source.lock();
65        let position = source.bot.position();
66        source.reply(format!(
67            "I'm at {}, {}, {}",
68            position.x, position.y, position.z
69        ));
70        1
71    };
72    commands.register(literal("whereareyou").executes(whereareyou));
73    commands.register(literal("pos").executes(whereareyou));
74
75    commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
76        let source = ctx.source.lock();
77        source.reply(format!(
78            "I am {} ({}, {})",
79            source.bot.username(),
80            source.bot.uuid(),
81            source.bot.entity
82        ));
83        1
84    }));
85
86    commands.register(literal("getdirection").executes(|ctx: &Ctx| {
87        let source = ctx.source.lock();
88        let direction = source.bot.direction();
89        source.reply(format!(
90            "I'm looking at {}, {}",
91            direction.y_rot(),
92            direction.x_rot()
93        ));
94        1
95    }));
96
97    commands.register(literal("health").executes(|ctx: &Ctx| {
98        let source = ctx.source.lock();
99
100        let health = source.bot.health();
101        source.reply(format!("I have {health} health"));
102        1
103    }));
104
105    commands.register(literal("lookingat").executes(|ctx: &Ctx| {
106        let source = ctx.source.lock();
107
108        let hit_result = source.bot.hit_result();
109
110        match &hit_result {
111            HitResult::Block(r) => {
112                if r.miss {
113                    source.reply("I'm not looking at anything");
114                    return 0;
115                }
116                let block_pos = r.block_pos;
117                let block = source.bot.world().read().get_block_state(block_pos);
118                source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
119            }
120            HitResult::Entity(r) => {
121                let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
122                source.reply(format!(
123                    "I'm looking at {entity_kind} ({:?}) at {}",
124                    r.entity, r.location
125                ));
126            }
127        }
128
129        1
130    }));
131
132    commands.register(literal("getblock").then(argument("x", integer()).then(
133        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
134            let source = ctx.source.lock();
135            let x = get_integer(ctx, "x").unwrap();
136            let y = get_integer(ctx, "y").unwrap();
137            let z = get_integer(ctx, "z").unwrap();
138            println!("getblock xyz {x} {y} {z}");
139            let block_pos = BlockPos::new(x, y, z);
140            let block = source.bot.world().read().get_block_state(block_pos);
141            source.reply(format!("BlockKind at {block_pos} is {block:?}"));
142            1
143        })),
144    )));
145    commands.register(literal("getfluid").then(argument("x", integer()).then(
146        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
147            let source = ctx.source.lock();
148            let x = get_integer(ctx, "x").unwrap();
149            let y = get_integer(ctx, "y").unwrap();
150            let z = get_integer(ctx, "z").unwrap();
151            println!("getfluid xyz {x} {y} {z}");
152            let block_pos = BlockPos::new(x, y, z);
153            let block = source.bot.world().read().get_fluid_state(block_pos);
154            source.reply(format!("Fluid at {block_pos} is {block:?}"));
155            1
156        })),
157    )));
158
159    commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
160        let source = ctx.source.lock();
161        let pathfinder = source.bot.get_component::<Pathfinder>();
162        let Some(pathfinder) = pathfinder else {
163            source.reply("I don't have the Pathfinder ocmponent");
164            return 1;
165        };
166        source.reply(format!(
167            "pathfinder.is_calculating: {}",
168            pathfinder.is_calculating
169        ));
170
171        let executing_path = source.bot.get_component::<ExecutingPath>();
172        let Some(executing_path) = executing_path else {
173            source.reply("I'm not executing a path");
174            return 1;
175        };
176        source.reply(format!(
177            "is_path_partial: {}, path.len: {}, queued_path.len: {}",
178            executing_path.is_path_partial,
179            executing_path.path.len(),
180            if let Some(queued) = &executing_path.queued_path {
181                queued.len().to_string()
182            } else {
183                "n/a".to_owned()
184            },
185        ));
186        1
187    }));
188
189    commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
190        let source = ctx.source.lock();
191        source.bot.start_use_item();
192        source.reply("Ok!");
193        1
194    }));
195    commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
196        let source = ctx.source.lock();
197        let max_stack_size = source
198            .bot
199            .get_held_item()
200            .get_component::<MaxStackSize>()
201            .map_or(-1, |s| s.count);
202        source.reply(format!("{max_stack_size}"));
203        1
204    }));
205
206    commands.register(literal("dimensions").executes(|ctx: &Ctx| {
207        let source = ctx.source.lock();
208        let bot_dimensions = source.bot.dimensions();
209        source.reply(format!("{bot_dimensions:?}"));
210        1
211    }));
212
213    commands.register(literal("players").executes(|ctx: &Ctx| {
214        let source = ctx.source.lock();
215        let player_entities = source
216            .bot
217            .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
218        let tab_list = source.bot.tab_list();
219        for player_entity in player_entities {
220            let uuid = player_entity.uuid();
221            source.reply(format!(
222                "{} - {} ({:?})",
223                player_entity.id(),
224                tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
225                uuid
226            ));
227        }
228        1
229    }));
230
231    commands.register(literal("enchants").executes(|ctx: &Ctx| {
232        let source = ctx.source.lock();
233        source.bot.with_registry_holder(|r| {
234            let enchants = &r.enchantment;
235            println!("enchants: {enchants:?}");
236        });
237        1
238    }));
239
240    commands.register(literal("attributes").executes(|ctx: &Ctx| {
241        let source = ctx.source.lock();
242        let attributes = source.bot.attributes();
243        println!("attributes: {attributes:?}");
244        1
245    }));
246
247    commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
248        let source = ctx.source.lock();
249
250        source.reply("Ok!");
251
252
253
254        source.bot.disconnect();
255
256        let ecs = source.bot.ecs.clone();
257        thread::spawn(move || {
258            thread::sleep(Duration::from_secs(1));
259            // dump the ecs
260
261            let mut ecs = ecs.write();
262
263            let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
264            let mut report = File::create(&report_path).unwrap();
265
266            let mut query = ecs.query::<EntityRef>();
267            for entity in query.iter(& ecs) {
268                writeln!(report, "Entity: {}", entity.id()).unwrap();
269                let archetype = entity.archetype();
270                let component_count = archetype.component_count();
271
272                let component_names = archetype
273                    .components()
274                    .iter()
275                    .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
276                    .collect::<Vec<_>>();
277                writeln!(
278                    report,
279                    "- {component_count} components: {}",
280                    component_names.join(", ")
281                )
282                .unwrap();
283            }
284
285            writeln!(report).unwrap();
286
287
288            for (info, _) in ecs.iter_resources() {
289                let name = info.name().to_string();
290                writeln!(report, "Resource: {name}").unwrap();
291                // writeln!(report, "- Size: {} bytes",
292                // info.layout().size()).unwrap();
293
294                match name.as_ref() {
295                    "azalea_world::container::InstanceContainer" => {
296                        let instance_container = ecs.resource::<InstanceContainer>();
297
298                        for (instance_name, instance) in &instance_container.instances {
299                            writeln!(report, "- Name: {instance_name}").unwrap();
300                            writeln!(report, "- Reference count: {}", instance.strong_count())
301                                .unwrap();
302                            if let Some(instance) = instance.upgrade() {
303                                let instance = instance.read();
304                                let strong_chunks = instance
305                                    .chunks
306                                    .map
307                                    .iter()
308                                    .filter(|(_, v)| v.strong_count() > 0)
309                                    .count();
310                                writeln!(
311                                    report,
312                                    "- Chunks: {} strongly referenced, {} in map",
313                                    strong_chunks,
314                                    instance.chunks.map.len()
315                                )
316                                .unwrap();
317                                writeln!(
318                                    report,
319                                    "- Entities: {}",
320                                    instance.entities_by_chunk.len()
321                                )
322                                .unwrap();
323                            }
324                        }
325                    }
326                    "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
327                        let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
328                        writeln!(report, "- Event count: {}", events.len()).unwrap();
329                    }
330                    "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
331                        let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
332                        writeln!(report, "- Event count: {}", events.len()).unwrap();
333                    }
334
335                    _ => {}
336                }
337            }
338
339            println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
340        });
341
342        1
343    }));
344
345    commands.register(literal("exit").executes(|ctx: &Ctx| {
346        let source = ctx.source.lock();
347        source.reply("bye!");
348
349        source.bot.disconnect();
350
351        let source = ctx.source.clone();
352        thread::spawn(move || {
353            thread::sleep(Duration::from_secs(1));
354
355            source
356                .lock()
357                .bot
358                .ecs
359                .write()
360                .write_message(AppExit::Success);
361        });
362
363        1
364    }));
365}
Source

pub fn then_built(self, argument: CommandNode<S>) -> Self

Add an already built child node to this node.

You should usually use Self::then instead.

Source

pub fn executes<F>(self, f: F) -> Self
where F: Fn(&CommandContext<S>) -> i32 + Send + Sync + 'static,

Set the command to be executed when this node is reached.

If this is not present on a node, it is not a valid command.

literal("foo").executes(|ctx: &CommandContext<()>| 42)
Examples found in repository?
azalea/examples/testbot/commands/combat.rs (lines 9-20)
7pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
8    commands.register(
9        literal("killaura").then(argument("enabled", bool()).executes(|ctx: &Ctx| {
10            let enabled = get_bool(ctx, "enabled").unwrap();
11            let source = ctx.source.lock();
12            let bot = source.bot.clone();
13            bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled);
14            source.reply(if enabled {
15                "Enabled killaura"
16            } else {
17                "Disabled killaura"
18            });
19            1
20        })),
21    );
22}
More examples
Hide additional examples
azalea/examples/testbot/commands/movement.rs (lines 17-31)
14pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
15    commands.register(
16        literal("goto")
17            .executes(|ctx: &Ctx| {
18                let mut source = ctx.source.lock();
19                println!("got goto");
20                // look for the sender
21                let Some(entity) = source.entity() else {
22                    source.reply("I can't see you!");
23                    return 0;
24                };
25                let position = entity.position();
26                source.reply("ok");
27                source
28                    .bot
29                    .start_goto(BlockPosGoal(BlockPos::from(position)));
30                1
31            })
32            .then(literal("xz").then(argument("x", integer()).then(
33                argument("z", integer()).executes(|ctx: &Ctx| {
34                    let source = ctx.source.lock();
35                    let x = get_integer(ctx, "x").unwrap();
36                    let z = get_integer(ctx, "z").unwrap();
37                    println!("goto xz {x} {z}");
38                    source.reply("ok");
39                    source.bot.start_goto(XZGoal { x, z });
40                    1
41                }),
42            )))
43            .then(literal("radius").then(argument("radius", float()).then(
44                argument("x", integer()).then(argument("y", integer()).then(
45                    argument("z", integer()).executes(|ctx: &Ctx| {
46                        let source = ctx.source.lock();
47                        let radius = get_float(ctx, "radius").unwrap();
48                        let x = get_integer(ctx, "x").unwrap();
49                        let y = get_integer(ctx, "y").unwrap();
50                        let z = get_integer(ctx, "z").unwrap();
51                        println!("goto radius {radius}, position: {x} {y} {z}");
52                        source.reply("ok");
53                        source.bot.start_goto(RadiusGoal {
54                            pos: BlockPos::new(x, y, z).center(),
55                            radius,
56                        });
57                        1
58                    }),
59                )),
60            )))
61            .then(argument("x", integer()).then(argument("y", integer()).then(
62                argument("z", integer()).executes(|ctx: &Ctx| {
63                    let source = ctx.source.lock();
64                    let x = get_integer(ctx, "x").unwrap();
65                    let y = get_integer(ctx, "y").unwrap();
66                    let z = get_integer(ctx, "z").unwrap();
67                    println!("goto xyz {x} {y} {z}");
68                    source.reply("ok");
69                    source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
70                    1
71                }),
72            ))),
73    );
74
75    commands.register(literal("down").executes(|ctx: &Ctx| {
76        let source = ctx.source.clone();
77        tokio::spawn(async move {
78            let bot = source.lock().bot.clone();
79            let position = BlockPos::from(bot.position());
80            source.lock().reply("mining...");
81            bot.mine(position.down(1)).await;
82            source.lock().reply("done");
83        });
84        1
85    }));
86
87    commands.register(
88        literal("look")
89            .executes(|ctx: &Ctx| {
90                // look for the sender
91                let mut source = ctx.source.lock();
92                let Some(entity) = source.entity() else {
93                    source.reply("I can't see you!");
94                    return 0;
95                };
96                let eye_position = entity.eye_position();
97                source.bot.look_at(eye_position);
98                1
99            })
100            .then(argument("x", integer()).then(argument("y", integer()).then(
101                argument("z", integer()).executes(|ctx: &Ctx| {
102                    let pos = BlockPos::new(
103                        get_integer(ctx, "x").unwrap(),
104                        get_integer(ctx, "y").unwrap(),
105                        get_integer(ctx, "z").unwrap(),
106                    );
107                    println!("{pos:?}");
108                    let source = ctx.source.lock();
109                    source.bot.look_at(pos.center());
110                    1
111                }),
112            ))),
113    );
114
115    commands.register(
116        literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
117            let mut seconds = get_float(ctx, "seconds").unwrap();
118            let source = ctx.source.lock();
119            let bot = source.bot.clone();
120
121            if seconds < 0. {
122                bot.walk(WalkDirection::Backward);
123                seconds = -seconds;
124            } else {
125                bot.walk(WalkDirection::Forward);
126            }
127
128            tokio::spawn(async move {
129                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
130                bot.walk(WalkDirection::None);
131            });
132            source.reply(format!("ok, walking for {seconds} seconds"));
133            1
134        })),
135    );
136    commands.register(
137        literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
138            let seconds = get_float(ctx, "seconds").unwrap();
139            let source = ctx.source.lock();
140            let bot = source.bot.clone();
141            bot.sprint(SprintDirection::Forward);
142            tokio::spawn(async move {
143                tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
144                bot.walk(WalkDirection::None);
145            });
146            source.reply(format!("ok, sprinting for {seconds} seconds"));
147            1
148        })),
149    );
150
151    commands.register(literal("north").executes(|ctx: &Ctx| {
152        let source = ctx.source.lock();
153        source.bot.set_direction(180., 0.);
154        source.reply("ok");
155        1
156    }));
157    commands.register(literal("south").executes(|ctx: &Ctx| {
158        let source = ctx.source.lock();
159        source.bot.set_direction(0., 0.);
160        source.reply("ok");
161        1
162    }));
163    commands.register(literal("east").executes(|ctx: &Ctx| {
164        let source = ctx.source.lock();
165        source.bot.set_direction(-90., 0.);
166        source.reply("ok");
167        1
168    }));
169    commands.register(literal("west").executes(|ctx: &Ctx| {
170        let source = ctx.source.lock();
171        source.bot.set_direction(90., 0.);
172        source.reply("ok");
173        1
174    }));
175    commands.register(
176        literal("jump")
177            .executes(|ctx: &Ctx| {
178                let source = ctx.source.lock();
179                source.bot.jump();
180                source.reply("ok");
181                1
182            })
183            .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
184                let jumping = get_bool(ctx, "enabled").unwrap();
185                let source = ctx.source.lock();
186                source.bot.set_jumping(jumping);
187                1
188            })),
189    );
190
191    let sneak = |ctx: &Ctx| {
192        let source = ctx.source.lock();
193        source.bot.set_crouching(!source.bot.crouching());
194        source.reply("ok");
195        1
196    };
197    let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
198        let sneaking = get_bool(ctx, "enabled").unwrap();
199        let source = ctx.source.lock();
200        source.bot.set_crouching(sneaking);
201        1
202    });
203    commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
204    commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
205
206    commands.register(literal("stop").executes(|ctx: &Ctx| {
207        let source = ctx.source.lock();
208        source.bot.stop_pathfinding();
209        source.reply("ok");
210        *source.state.task.lock() = BotTask::None;
211        1
212    }));
213}
azalea/examples/testbot/commands/debug.rs (lines 23-27)
22pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
23    commands.register(literal("ping").executes(|ctx: &Ctx| {
24        let source = ctx.source.lock();
25        source.reply("pong!");
26        1
27    }));
28
29    commands.register(literal("disconnect").executes(|ctx: &Ctx| {
30        let source = ctx.source.lock();
31        source.bot.disconnect();
32        1
33    }));
34
35    commands.register(literal("whereami").executes(|ctx: &Ctx| {
36        let mut source = ctx.source.lock();
37        let Some(entity) = source.entity() else {
38            source.reply("You aren't in render distance!");
39            return 0;
40        };
41        let position = entity.position();
42        source.reply(format!(
43            "You are at {}, {}, {}",
44            position.x, position.y, position.z
45        ));
46        1
47    }));
48
49    commands.register(literal("entityid").executes(|ctx: &Ctx| {
50        let mut source = ctx.source.lock();
51        let Some(entity) = source.entity() else {
52            source.reply("You aren't in render distance!");
53            return 0;
54        };
55        let entity_id = entity.minecraft_id();
56        source.reply(format!(
57            "Your Minecraft ID is {} and your ECS ID is {entity:?}",
58            *entity_id
59        ));
60        1
61    }));
62
63    let whereareyou = |ctx: &Ctx| {
64        let source = ctx.source.lock();
65        let position = source.bot.position();
66        source.reply(format!(
67            "I'm at {}, {}, {}",
68            position.x, position.y, position.z
69        ));
70        1
71    };
72    commands.register(literal("whereareyou").executes(whereareyou));
73    commands.register(literal("pos").executes(whereareyou));
74
75    commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
76        let source = ctx.source.lock();
77        source.reply(format!(
78            "I am {} ({}, {})",
79            source.bot.username(),
80            source.bot.uuid(),
81            source.bot.entity
82        ));
83        1
84    }));
85
86    commands.register(literal("getdirection").executes(|ctx: &Ctx| {
87        let source = ctx.source.lock();
88        let direction = source.bot.direction();
89        source.reply(format!(
90            "I'm looking at {}, {}",
91            direction.y_rot(),
92            direction.x_rot()
93        ));
94        1
95    }));
96
97    commands.register(literal("health").executes(|ctx: &Ctx| {
98        let source = ctx.source.lock();
99
100        let health = source.bot.health();
101        source.reply(format!("I have {health} health"));
102        1
103    }));
104
105    commands.register(literal("lookingat").executes(|ctx: &Ctx| {
106        let source = ctx.source.lock();
107
108        let hit_result = source.bot.hit_result();
109
110        match &hit_result {
111            HitResult::Block(r) => {
112                if r.miss {
113                    source.reply("I'm not looking at anything");
114                    return 0;
115                }
116                let block_pos = r.block_pos;
117                let block = source.bot.world().read().get_block_state(block_pos);
118                source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
119            }
120            HitResult::Entity(r) => {
121                let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
122                source.reply(format!(
123                    "I'm looking at {entity_kind} ({:?}) at {}",
124                    r.entity, r.location
125                ));
126            }
127        }
128
129        1
130    }));
131
132    commands.register(literal("getblock").then(argument("x", integer()).then(
133        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
134            let source = ctx.source.lock();
135            let x = get_integer(ctx, "x").unwrap();
136            let y = get_integer(ctx, "y").unwrap();
137            let z = get_integer(ctx, "z").unwrap();
138            println!("getblock xyz {x} {y} {z}");
139            let block_pos = BlockPos::new(x, y, z);
140            let block = source.bot.world().read().get_block_state(block_pos);
141            source.reply(format!("BlockKind at {block_pos} is {block:?}"));
142            1
143        })),
144    )));
145    commands.register(literal("getfluid").then(argument("x", integer()).then(
146        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
147            let source = ctx.source.lock();
148            let x = get_integer(ctx, "x").unwrap();
149            let y = get_integer(ctx, "y").unwrap();
150            let z = get_integer(ctx, "z").unwrap();
151            println!("getfluid xyz {x} {y} {z}");
152            let block_pos = BlockPos::new(x, y, z);
153            let block = source.bot.world().read().get_fluid_state(block_pos);
154            source.reply(format!("Fluid at {block_pos} is {block:?}"));
155            1
156        })),
157    )));
158
159    commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
160        let source = ctx.source.lock();
161        let pathfinder = source.bot.get_component::<Pathfinder>();
162        let Some(pathfinder) = pathfinder else {
163            source.reply("I don't have the Pathfinder ocmponent");
164            return 1;
165        };
166        source.reply(format!(
167            "pathfinder.is_calculating: {}",
168            pathfinder.is_calculating
169        ));
170
171        let executing_path = source.bot.get_component::<ExecutingPath>();
172        let Some(executing_path) = executing_path else {
173            source.reply("I'm not executing a path");
174            return 1;
175        };
176        source.reply(format!(
177            "is_path_partial: {}, path.len: {}, queued_path.len: {}",
178            executing_path.is_path_partial,
179            executing_path.path.len(),
180            if let Some(queued) = &executing_path.queued_path {
181                queued.len().to_string()
182            } else {
183                "n/a".to_owned()
184            },
185        ));
186        1
187    }));
188
189    commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
190        let source = ctx.source.lock();
191        source.bot.start_use_item();
192        source.reply("Ok!");
193        1
194    }));
195    commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
196        let source = ctx.source.lock();
197        let max_stack_size = source
198            .bot
199            .get_held_item()
200            .get_component::<MaxStackSize>()
201            .map_or(-1, |s| s.count);
202        source.reply(format!("{max_stack_size}"));
203        1
204    }));
205
206    commands.register(literal("dimensions").executes(|ctx: &Ctx| {
207        let source = ctx.source.lock();
208        let bot_dimensions = source.bot.dimensions();
209        source.reply(format!("{bot_dimensions:?}"));
210        1
211    }));
212
213    commands.register(literal("players").executes(|ctx: &Ctx| {
214        let source = ctx.source.lock();
215        let player_entities = source
216            .bot
217            .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
218        let tab_list = source.bot.tab_list();
219        for player_entity in player_entities {
220            let uuid = player_entity.uuid();
221            source.reply(format!(
222                "{} - {} ({:?})",
223                player_entity.id(),
224                tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
225                uuid
226            ));
227        }
228        1
229    }));
230
231    commands.register(literal("enchants").executes(|ctx: &Ctx| {
232        let source = ctx.source.lock();
233        source.bot.with_registry_holder(|r| {
234            let enchants = &r.enchantment;
235            println!("enchants: {enchants:?}");
236        });
237        1
238    }));
239
240    commands.register(literal("attributes").executes(|ctx: &Ctx| {
241        let source = ctx.source.lock();
242        let attributes = source.bot.attributes();
243        println!("attributes: {attributes:?}");
244        1
245    }));
246
247    commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
248        let source = ctx.source.lock();
249
250        source.reply("Ok!");
251
252
253
254        source.bot.disconnect();
255
256        let ecs = source.bot.ecs.clone();
257        thread::spawn(move || {
258            thread::sleep(Duration::from_secs(1));
259            // dump the ecs
260
261            let mut ecs = ecs.write();
262
263            let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
264            let mut report = File::create(&report_path).unwrap();
265
266            let mut query = ecs.query::<EntityRef>();
267            for entity in query.iter(& ecs) {
268                writeln!(report, "Entity: {}", entity.id()).unwrap();
269                let archetype = entity.archetype();
270                let component_count = archetype.component_count();
271
272                let component_names = archetype
273                    .components()
274                    .iter()
275                    .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
276                    .collect::<Vec<_>>();
277                writeln!(
278                    report,
279                    "- {component_count} components: {}",
280                    component_names.join(", ")
281                )
282                .unwrap();
283            }
284
285            writeln!(report).unwrap();
286
287
288            for (info, _) in ecs.iter_resources() {
289                let name = info.name().to_string();
290                writeln!(report, "Resource: {name}").unwrap();
291                // writeln!(report, "- Size: {} bytes",
292                // info.layout().size()).unwrap();
293
294                match name.as_ref() {
295                    "azalea_world::container::InstanceContainer" => {
296                        let instance_container = ecs.resource::<InstanceContainer>();
297
298                        for (instance_name, instance) in &instance_container.instances {
299                            writeln!(report, "- Name: {instance_name}").unwrap();
300                            writeln!(report, "- Reference count: {}", instance.strong_count())
301                                .unwrap();
302                            if let Some(instance) = instance.upgrade() {
303                                let instance = instance.read();
304                                let strong_chunks = instance
305                                    .chunks
306                                    .map
307                                    .iter()
308                                    .filter(|(_, v)| v.strong_count() > 0)
309                                    .count();
310                                writeln!(
311                                    report,
312                                    "- Chunks: {} strongly referenced, {} in map",
313                                    strong_chunks,
314                                    instance.chunks.map.len()
315                                )
316                                .unwrap();
317                                writeln!(
318                                    report,
319                                    "- Entities: {}",
320                                    instance.entities_by_chunk.len()
321                                )
322                                .unwrap();
323                            }
324                        }
325                    }
326                    "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
327                        let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
328                        writeln!(report, "- Event count: {}", events.len()).unwrap();
329                    }
330                    "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
331                        let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
332                        writeln!(report, "- Event count: {}", events.len()).unwrap();
333                    }
334
335                    _ => {}
336                }
337            }
338
339            println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
340        });
341
342        1
343    }));
344
345    commands.register(literal("exit").executes(|ctx: &Ctx| {
346        let source = ctx.source.lock();
347        source.reply("bye!");
348
349        source.bot.disconnect();
350
351        let source = ctx.source.clone();
352        thread::spawn(move || {
353            thread::sleep(Duration::from_secs(1));
354
355            source
356                .lock()
357                .bot
358                .ecs
359                .write()
360                .write_message(AppExit::Success);
361        });
362
363        1
364    }));
365}
Source

pub fn executes_result<F>(self, f: F) -> Self
where F: Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync + 'static,

Same as Self::executes but returns a Result<i32, CommandSyntaxError>.

Source

pub fn requires<F>(self, requirement: F) -> Self
where F: Fn(&S) -> bool + Send + Sync + 'static,

Set the requirement for this node to be considered.

If this is not present on a node, it is considered to always pass.

literal("foo")
    .requires(|s: &CommandSource| s.opped)
    // ...
Source

pub fn redirect(self, target: Arc<RwLock<CommandNode<S>>>) -> Self

Source

pub fn fork( self, target: Arc<RwLock<CommandNode<S>>>, modifier: Arc<RedirectModifier<S>>, ) -> Self

Source

pub fn forward( self, target: Arc<RwLock<CommandNode<S>>>, modifier: Option<Arc<RedirectModifier<S>>>, fork: bool, ) -> Self

Source

pub fn arguments(&self) -> &CommandNode<S>

Source

pub fn build(self) -> CommandNode<S>

Manually build this node into a CommandNode. You probably don’t need to do this yourself.

Trait Implementations§

Source§

impl<S> Clone for ArgumentBuilder<S>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<S> Debug for ArgumentBuilder<S>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<S> Freeze for ArgumentBuilder<S>

§

impl<S> !RefUnwindSafe for ArgumentBuilder<S>

§

impl<S> Send for ArgumentBuilder<S>

§

impl<S> Sync for ArgumentBuilder<S>

§

impl<S> Unpin for ArgumentBuilder<S>

§

impl<S> !UnwindSafe for ArgumentBuilder<S>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more