pub struct EntityRef { /* private fields */ }Expand description
A reference to an entity in a world.
This is different from [Entity], since you can perform actions with just
an EntityRef instead of it only being an identifier.
Most functions on EntityRef that return a value will result in a panic if
the client has despawned, so if your code involves waiting, you should check
Self::is_alive or Self::exists before calling those functions.
Also, since EntityRef stores the Client alongside the entity, this
means that it supports interactions such as Self::attack.
Not to be confused with Bevy’s EntityRef.
Implementations§
Source§impl EntityRef
impl EntityRef
Sourcepub fn position(&self) -> Vec3
pub fn position(&self) -> Vec3
Get the entity’s position in the world, which is the same as its feet position.
To get the client’s eye position, use Self::eye_position.
Also see Client::position.
Examples found in repository?
14pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
15 commands.register(
16 literal("goto")
17 .executes(|ctx: &Ctx| {
18 let 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.up(0.5))));
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 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 commands.register(literal("forcestop").executes(|ctx: &Ctx| {
214 let source = ctx.source.lock();
215 source.bot.force_stop_pathfinding();
216 source.reply("ok");
217 *source.state.task.lock() = BotTask::None;
218 1
219 }));
220}More examples
26pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
27 commands.register(literal("ping").executes(|ctx: &Ctx| {
28 let source = ctx.source.lock();
29 source.reply("pong!");
30 1
31 }));
32
33 commands.register(literal("disconnect").executes(|ctx: &Ctx| {
34 let source = ctx.source.lock();
35 source.bot.disconnect();
36 1
37 }));
38
39 commands.register(literal("whereami").executes(|ctx: &Ctx| {
40 let source = ctx.source.lock();
41 let Some(entity) = source.entity() else {
42 source.reply("You aren't in render distance!");
43 return 0;
44 };
45 let position = entity.position();
46 source.reply(format!(
47 "You are at {}, {}, {}",
48 position.x, position.y, position.z
49 ));
50 1
51 }));
52
53 commands.register(literal("entityid").executes(|ctx: &Ctx| {
54 let source = ctx.source.lock();
55 let Some(entity) = source.entity() else {
56 source.reply("You aren't in render distance!");
57 return 0;
58 };
59 let entity_id = entity.minecraft_id();
60 source.reply(format!(
61 "Your Minecraft ID is {} and your ECS ID is {entity:?}",
62 *entity_id
63 ));
64 1
65 }));
66
67 let whereareyou = |ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let position = source.bot.position();
70 source.reply(format!(
71 "I'm at {}, {}, {}",
72 position.x, position.y, position.z
73 ));
74 1
75 };
76 commands.register(literal("whereareyou").executes(whereareyou));
77 commands.register(literal("pos").executes(whereareyou));
78
79 commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
80 let source = ctx.source.lock();
81 source.reply(format!(
82 "I am {} ({}, {})",
83 source.bot.username(),
84 source.bot.uuid(),
85 source.bot.entity
86 ));
87 1
88 }));
89
90 commands.register(literal("getdirection").executes(|ctx: &Ctx| {
91 let source = ctx.source.lock();
92 let direction = source.bot.direction();
93 source.reply(format!(
94 "I'm looking at {}, {}",
95 direction.y_rot(),
96 direction.x_rot()
97 ));
98 1
99 }));
100
101 commands.register(literal("health").executes(|ctx: &Ctx| {
102 let source = ctx.source.lock();
103
104 let health = source.bot.health();
105 source.reply(format!("I have {health} health"));
106 1
107 }));
108
109 commands.register(literal("lookingat").executes(|ctx: &Ctx| {
110 let source = ctx.source.lock();
111
112 let hit_result = source.bot.hit_result();
113
114 match &hit_result {
115 HitResult::Block(r) => {
116 if r.miss {
117 source.reply("I'm not looking at anything");
118 return 0;
119 }
120 let block_pos = r.block_pos;
121 let block = source.bot.world().read().get_block_state(block_pos);
122 source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
123 }
124 HitResult::Entity(r) => {
125 let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
126 source.reply(format!(
127 "I'm looking at {entity_kind} ({:?}) at {}",
128 r.entity, r.location
129 ));
130 }
131 }
132
133 1
134 }));
135
136 commands.register(literal("getblock").then(argument("x", integer()).then(
137 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
138 let source = ctx.source.lock();
139 let x = get_integer(ctx, "x").unwrap();
140 let y = get_integer(ctx, "y").unwrap();
141 let z = get_integer(ctx, "z").unwrap();
142 println!("getblock xyz {x} {y} {z}");
143 let block_pos = BlockPos::new(x, y, z);
144 let block = source.bot.world().read().get_block_state(block_pos);
145 source.reply(format!("BlockKind at {block_pos} is {block:?}"));
146 1
147 })),
148 )));
149 commands.register(literal("getfluid").then(argument("x", integer()).then(
150 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
151 let source = ctx.source.lock();
152 let x = get_integer(ctx, "x").unwrap();
153 let y = get_integer(ctx, "y").unwrap();
154 let z = get_integer(ctx, "z").unwrap();
155 println!("getfluid xyz {x} {y} {z}");
156 let block_pos = BlockPos::new(x, y, z);
157 let block = source.bot.world().read().get_fluid_state(block_pos);
158 source.reply(format!("Fluid at {block_pos} is {block:?}"));
159 1
160 })),
161 )));
162
163 commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
164 let source = ctx.source.lock();
165 let pathfinder = source.bot.get_component::<Pathfinder>();
166 let Some(pathfinder) = pathfinder else {
167 source.reply("I don't have the Pathfinder component");
168 return 1;
169 };
170 source.reply(format!(
171 "pathfinder.is_calculating: {}",
172 pathfinder.is_calculating
173 ));
174
175 let executing_path = source.bot.get_component::<ExecutingPath>();
176 let Some(executing_path) = executing_path else {
177 source.reply("I'm not executing a path");
178 return 1;
179 };
180 source.reply(format!(
181 "is_path_partial: {}, path.len: {}, queued_path.len: {}",
182 executing_path.is_path_partial,
183 executing_path.path.len(),
184 if let Some(queued) = &executing_path.queued_path {
185 queued.len().to_string()
186 } else {
187 "n/a".to_owned()
188 },
189 ));
190 1
191 }));
192 commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
193 let source = ctx.source.lock();
194
195 let Some(entity) = source.entity() else {
196 source.reply("You aren't in render distance!");
197 return 0;
198 };
199 let position = entity.position();
200 let position = BlockPos::from(position);
201
202 let mut edges = Vec::new();
203 let cached_world = CachedWorld::new(source.bot.world(), position);
204 let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
205 let custom_state = CustomPathfinderStateRef::default();
206
207 azalea::pathfinder::moves::default_move(
208 &mut MovesCtx {
209 edges: &mut edges,
210 world: &cached_world,
211 mining_cache: &mining_cache,
212 custom_state: &custom_state,
213 },
214 RelBlockPos::from_origin(position, position),
215 );
216
217 if edges.is_empty() {
218 source.reply("No possible moves.");
219 } else {
220 source.reply("Moves:");
221 for (i, edge) in edges.iter().enumerate() {
222 source.reply(format!("{}) {edge:?}", i + 1));
223 }
224 }
225
226 1
227 }));
228
229 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
230 let source = ctx.source.lock();
231 source.bot.start_use_item();
232 source.reply("Ok!");
233 1
234 }));
235 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 let max_stack_size = source
238 .bot
239 .get_held_item()
240 .get_component::<MaxStackSize>()
241 .map_or(-1, |s| s.count);
242 source.reply(format!("{max_stack_size}"));
243 1
244 }));
245
246 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
247 let source = ctx.source.lock();
248 let bot_dimensions = source.bot.dimensions();
249 source.reply(format!("{bot_dimensions:?}"));
250 1
251 }));
252
253 commands.register(literal("players").executes(|ctx: &Ctx| {
254 let source = ctx.source.lock();
255 let player_entities = source
256 .bot
257 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
258 let tab_list = source.bot.tab_list();
259 for player_entity in player_entities {
260 let uuid = player_entity.uuid();
261 source.reply(format!(
262 "{} - {} ({:?})",
263 player_entity.id(),
264 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
265 uuid
266 ));
267 }
268 1
269 }));
270
271 commands.register(literal("enchants").executes(|ctx: &Ctx| {
272 let source = ctx.source.lock();
273 source.bot.with_registry_holder(|r| {
274 let enchants = &r.enchantment;
275 println!("enchants: {enchants:?}");
276 });
277 1
278 }));
279
280 commands.register(literal("attributes").executes(|ctx: &Ctx| {
281 let source = ctx.source.lock();
282 let attributes = source.bot.attributes();
283 println!("attributes: {attributes:?}");
284 1
285 }));
286
287 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
288 let source = ctx.source.lock();
289
290 source.reply("Ok!");
291
292
293
294 source.bot.disconnect();
295
296 let ecs = source.bot.ecs.clone();
297 thread::spawn(move || {
298 thread::sleep(Duration::from_secs(1));
299 // dump the ecs
300
301 let mut ecs = ecs.write();
302
303 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
304 let mut report = File::create(&report_path).unwrap();
305
306 let mut query = ecs.query::<EntityRef>();
307 for entity in query.iter(& ecs) {
308 writeln!(report, "Entity: {}", entity.id()).unwrap();
309 let archetype = entity.archetype();
310 let component_count = archetype.component_count();
311
312 let component_names = archetype
313 .components()
314 .iter()
315 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
316 .collect::<Vec<_>>();
317 writeln!(
318 report,
319 "- {component_count} components: {}",
320 component_names.join(", ")
321 )
322 .unwrap();
323 }
324
325 writeln!(report).unwrap();
326
327
328 for (info, _) in ecs.iter_resources() {
329 let name = info.name().to_string();
330 writeln!(report, "Resource: {name}").unwrap();
331 // writeln!(report, "- Size: {} bytes",
332 // info.layout().size()).unwrap();
333
334 match name.as_ref() {
335 "azalea_world::container::Worlds" => {
336 let worlds = ecs.resource::<Worlds>();
337
338 for (world_name, world) in &worlds.map {
339 writeln!(report, "- Name: {world_name}").unwrap();
340 writeln!(report, "- Reference count: {}", world.strong_count())
341 .unwrap();
342 if let Some(world) = world.upgrade() {
343 let world = world.read();
344 let strong_chunks = world
345 .chunks
346 .map
347 .iter()
348 .filter(|(_, v)| v.strong_count() > 0)
349 .count();
350 writeln!(
351 report,
352 "- Chunks: {} strongly referenced, {} in map",
353 strong_chunks,
354 world.chunks.map.len()
355 )
356 .unwrap();
357 writeln!(
358 report,
359 "- Entities: {}",
360 world.entities_by_chunk.len()
361 )
362 .unwrap();
363 }
364 }
365 }
366 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
367 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
368 writeln!(report, "- Event count: {}", events.len()).unwrap();
369 }
370 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
371 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
372 writeln!(report, "- Event count: {}", events.len()).unwrap();
373 }
374
375 _ => {}
376 }
377 }
378
379 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
380 });
381
382 1
383 }));
384
385 commands.register(literal("exit").executes(|ctx: &Ctx| {
386 let source = ctx.source.lock();
387 source.reply("bye!");
388
389 source.bot.disconnect();
390
391 let source = ctx.source.clone();
392 thread::spawn(move || {
393 thread::sleep(Duration::from_secs(1));
394
395 source
396 .lock()
397 .bot
398 .ecs
399 .write()
400 .write_message(AppExit::Success);
401 });
402
403 1
404 }));
405}Source§impl EntityRef
impl EntityRef
Sourcepub fn dimensions(&self) -> EntityDimensions
pub fn dimensions(&self) -> EntityDimensions
Get the bounding box dimensions for the entity, which contains its width, height, and eye height.
Also see Client::dimensions
Source§impl EntityRef
impl EntityRef
Sourcepub fn eye_position(&self) -> Vec3
pub fn eye_position(&self) -> Vec3
Get the position of this entity’s eyes.
Also see Client::eye_position.
Examples found in repository?
14pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
15 commands.register(
16 literal("goto")
17 .executes(|ctx: &Ctx| {
18 let 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.up(0.5))));
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 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 commands.register(literal("forcestop").executes(|ctx: &Ctx| {
214 let source = ctx.source.lock();
215 source.bot.force_stop_pathfinding();
216 source.reply("ok");
217 *source.state.task.lock() = BotTask::None;
218 1
219 }));
220}Source§impl EntityRef
impl EntityRef
Sourcepub fn health(&self) -> f32
pub fn health(&self) -> f32
Get the health of this entity, typically in the range 0..=20.
Also see Client::health.
Source§impl EntityRef
impl EntityRef
Sourcepub fn uuid(&self) -> Uuid
pub fn uuid(&self) -> Uuid
Get the Minecraft UUID of this entity.
Also see Client::uuid.
Examples found in repository?
26pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
27 commands.register(literal("ping").executes(|ctx: &Ctx| {
28 let source = ctx.source.lock();
29 source.reply("pong!");
30 1
31 }));
32
33 commands.register(literal("disconnect").executes(|ctx: &Ctx| {
34 let source = ctx.source.lock();
35 source.bot.disconnect();
36 1
37 }));
38
39 commands.register(literal("whereami").executes(|ctx: &Ctx| {
40 let source = ctx.source.lock();
41 let Some(entity) = source.entity() else {
42 source.reply("You aren't in render distance!");
43 return 0;
44 };
45 let position = entity.position();
46 source.reply(format!(
47 "You are at {}, {}, {}",
48 position.x, position.y, position.z
49 ));
50 1
51 }));
52
53 commands.register(literal("entityid").executes(|ctx: &Ctx| {
54 let source = ctx.source.lock();
55 let Some(entity) = source.entity() else {
56 source.reply("You aren't in render distance!");
57 return 0;
58 };
59 let entity_id = entity.minecraft_id();
60 source.reply(format!(
61 "Your Minecraft ID is {} and your ECS ID is {entity:?}",
62 *entity_id
63 ));
64 1
65 }));
66
67 let whereareyou = |ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let position = source.bot.position();
70 source.reply(format!(
71 "I'm at {}, {}, {}",
72 position.x, position.y, position.z
73 ));
74 1
75 };
76 commands.register(literal("whereareyou").executes(whereareyou));
77 commands.register(literal("pos").executes(whereareyou));
78
79 commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
80 let source = ctx.source.lock();
81 source.reply(format!(
82 "I am {} ({}, {})",
83 source.bot.username(),
84 source.bot.uuid(),
85 source.bot.entity
86 ));
87 1
88 }));
89
90 commands.register(literal("getdirection").executes(|ctx: &Ctx| {
91 let source = ctx.source.lock();
92 let direction = source.bot.direction();
93 source.reply(format!(
94 "I'm looking at {}, {}",
95 direction.y_rot(),
96 direction.x_rot()
97 ));
98 1
99 }));
100
101 commands.register(literal("health").executes(|ctx: &Ctx| {
102 let source = ctx.source.lock();
103
104 let health = source.bot.health();
105 source.reply(format!("I have {health} health"));
106 1
107 }));
108
109 commands.register(literal("lookingat").executes(|ctx: &Ctx| {
110 let source = ctx.source.lock();
111
112 let hit_result = source.bot.hit_result();
113
114 match &hit_result {
115 HitResult::Block(r) => {
116 if r.miss {
117 source.reply("I'm not looking at anything");
118 return 0;
119 }
120 let block_pos = r.block_pos;
121 let block = source.bot.world().read().get_block_state(block_pos);
122 source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
123 }
124 HitResult::Entity(r) => {
125 let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
126 source.reply(format!(
127 "I'm looking at {entity_kind} ({:?}) at {}",
128 r.entity, r.location
129 ));
130 }
131 }
132
133 1
134 }));
135
136 commands.register(literal("getblock").then(argument("x", integer()).then(
137 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
138 let source = ctx.source.lock();
139 let x = get_integer(ctx, "x").unwrap();
140 let y = get_integer(ctx, "y").unwrap();
141 let z = get_integer(ctx, "z").unwrap();
142 println!("getblock xyz {x} {y} {z}");
143 let block_pos = BlockPos::new(x, y, z);
144 let block = source.bot.world().read().get_block_state(block_pos);
145 source.reply(format!("BlockKind at {block_pos} is {block:?}"));
146 1
147 })),
148 )));
149 commands.register(literal("getfluid").then(argument("x", integer()).then(
150 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
151 let source = ctx.source.lock();
152 let x = get_integer(ctx, "x").unwrap();
153 let y = get_integer(ctx, "y").unwrap();
154 let z = get_integer(ctx, "z").unwrap();
155 println!("getfluid xyz {x} {y} {z}");
156 let block_pos = BlockPos::new(x, y, z);
157 let block = source.bot.world().read().get_fluid_state(block_pos);
158 source.reply(format!("Fluid at {block_pos} is {block:?}"));
159 1
160 })),
161 )));
162
163 commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
164 let source = ctx.source.lock();
165 let pathfinder = source.bot.get_component::<Pathfinder>();
166 let Some(pathfinder) = pathfinder else {
167 source.reply("I don't have the Pathfinder component");
168 return 1;
169 };
170 source.reply(format!(
171 "pathfinder.is_calculating: {}",
172 pathfinder.is_calculating
173 ));
174
175 let executing_path = source.bot.get_component::<ExecutingPath>();
176 let Some(executing_path) = executing_path else {
177 source.reply("I'm not executing a path");
178 return 1;
179 };
180 source.reply(format!(
181 "is_path_partial: {}, path.len: {}, queued_path.len: {}",
182 executing_path.is_path_partial,
183 executing_path.path.len(),
184 if let Some(queued) = &executing_path.queued_path {
185 queued.len().to_string()
186 } else {
187 "n/a".to_owned()
188 },
189 ));
190 1
191 }));
192 commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
193 let source = ctx.source.lock();
194
195 let Some(entity) = source.entity() else {
196 source.reply("You aren't in render distance!");
197 return 0;
198 };
199 let position = entity.position();
200 let position = BlockPos::from(position);
201
202 let mut edges = Vec::new();
203 let cached_world = CachedWorld::new(source.bot.world(), position);
204 let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
205 let custom_state = CustomPathfinderStateRef::default();
206
207 azalea::pathfinder::moves::default_move(
208 &mut MovesCtx {
209 edges: &mut edges,
210 world: &cached_world,
211 mining_cache: &mining_cache,
212 custom_state: &custom_state,
213 },
214 RelBlockPos::from_origin(position, position),
215 );
216
217 if edges.is_empty() {
218 source.reply("No possible moves.");
219 } else {
220 source.reply("Moves:");
221 for (i, edge) in edges.iter().enumerate() {
222 source.reply(format!("{}) {edge:?}", i + 1));
223 }
224 }
225
226 1
227 }));
228
229 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
230 let source = ctx.source.lock();
231 source.bot.start_use_item();
232 source.reply("Ok!");
233 1
234 }));
235 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 let max_stack_size = source
238 .bot
239 .get_held_item()
240 .get_component::<MaxStackSize>()
241 .map_or(-1, |s| s.count);
242 source.reply(format!("{max_stack_size}"));
243 1
244 }));
245
246 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
247 let source = ctx.source.lock();
248 let bot_dimensions = source.bot.dimensions();
249 source.reply(format!("{bot_dimensions:?}"));
250 1
251 }));
252
253 commands.register(literal("players").executes(|ctx: &Ctx| {
254 let source = ctx.source.lock();
255 let player_entities = source
256 .bot
257 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
258 let tab_list = source.bot.tab_list();
259 for player_entity in player_entities {
260 let uuid = player_entity.uuid();
261 source.reply(format!(
262 "{} - {} ({:?})",
263 player_entity.id(),
264 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
265 uuid
266 ));
267 }
268 1
269 }));
270
271 commands.register(literal("enchants").executes(|ctx: &Ctx| {
272 let source = ctx.source.lock();
273 source.bot.with_registry_holder(|r| {
274 let enchants = &r.enchantment;
275 println!("enchants: {enchants:?}");
276 });
277 1
278 }));
279
280 commands.register(literal("attributes").executes(|ctx: &Ctx| {
281 let source = ctx.source.lock();
282 let attributes = source.bot.attributes();
283 println!("attributes: {attributes:?}");
284 1
285 }));
286
287 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
288 let source = ctx.source.lock();
289
290 source.reply("Ok!");
291
292
293
294 source.bot.disconnect();
295
296 let ecs = source.bot.ecs.clone();
297 thread::spawn(move || {
298 thread::sleep(Duration::from_secs(1));
299 // dump the ecs
300
301 let mut ecs = ecs.write();
302
303 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
304 let mut report = File::create(&report_path).unwrap();
305
306 let mut query = ecs.query::<EntityRef>();
307 for entity in query.iter(& ecs) {
308 writeln!(report, "Entity: {}", entity.id()).unwrap();
309 let archetype = entity.archetype();
310 let component_count = archetype.component_count();
311
312 let component_names = archetype
313 .components()
314 .iter()
315 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
316 .collect::<Vec<_>>();
317 writeln!(
318 report,
319 "- {component_count} components: {}",
320 component_names.join(", ")
321 )
322 .unwrap();
323 }
324
325 writeln!(report).unwrap();
326
327
328 for (info, _) in ecs.iter_resources() {
329 let name = info.name().to_string();
330 writeln!(report, "Resource: {name}").unwrap();
331 // writeln!(report, "- Size: {} bytes",
332 // info.layout().size()).unwrap();
333
334 match name.as_ref() {
335 "azalea_world::container::Worlds" => {
336 let worlds = ecs.resource::<Worlds>();
337
338 for (world_name, world) in &worlds.map {
339 writeln!(report, "- Name: {world_name}").unwrap();
340 writeln!(report, "- Reference count: {}", world.strong_count())
341 .unwrap();
342 if let Some(world) = world.upgrade() {
343 let world = world.read();
344 let strong_chunks = world
345 .chunks
346 .map
347 .iter()
348 .filter(|(_, v)| v.strong_count() > 0)
349 .count();
350 writeln!(
351 report,
352 "- Chunks: {} strongly referenced, {} in map",
353 strong_chunks,
354 world.chunks.map.len()
355 )
356 .unwrap();
357 writeln!(
358 report,
359 "- Entities: {}",
360 world.entities_by_chunk.len()
361 )
362 .unwrap();
363 }
364 }
365 }
366 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
367 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
368 writeln!(report, "- Event count: {}", events.len()).unwrap();
369 }
370 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
371 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
372 writeln!(report, "- Event count: {}", events.len()).unwrap();
373 }
374
375 _ => {}
376 }
377 }
378
379 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
380 });
381
382 1
383 }));
384
385 commands.register(literal("exit").executes(|ctx: &Ctx| {
386 let source = ctx.source.lock();
387 source.reply("bye!");
388
389 source.bot.disconnect();
390
391 let source = ctx.source.clone();
392 thread::spawn(move || {
393 thread::sleep(Duration::from_secs(1));
394
395 source
396 .lock()
397 .bot
398 .ecs
399 .write()
400 .write_message(AppExit::Success);
401 });
402
403 1
404 }));
405}Source§impl EntityRef
impl EntityRef
Sourcepub fn minecraft_id(&self) -> MinecraftEntityId
pub fn minecraft_id(&self) -> MinecraftEntityId
Get the Minecraft UUID of this entity.
See [MinecraftEntityId] for more details. For persistent identifiers,
consider using Self::uuid instead.
Also see Client::minecraft_id.
Examples found in repository?
26pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
27 commands.register(literal("ping").executes(|ctx: &Ctx| {
28 let source = ctx.source.lock();
29 source.reply("pong!");
30 1
31 }));
32
33 commands.register(literal("disconnect").executes(|ctx: &Ctx| {
34 let source = ctx.source.lock();
35 source.bot.disconnect();
36 1
37 }));
38
39 commands.register(literal("whereami").executes(|ctx: &Ctx| {
40 let source = ctx.source.lock();
41 let Some(entity) = source.entity() else {
42 source.reply("You aren't in render distance!");
43 return 0;
44 };
45 let position = entity.position();
46 source.reply(format!(
47 "You are at {}, {}, {}",
48 position.x, position.y, position.z
49 ));
50 1
51 }));
52
53 commands.register(literal("entityid").executes(|ctx: &Ctx| {
54 let source = ctx.source.lock();
55 let Some(entity) = source.entity() else {
56 source.reply("You aren't in render distance!");
57 return 0;
58 };
59 let entity_id = entity.minecraft_id();
60 source.reply(format!(
61 "Your Minecraft ID is {} and your ECS ID is {entity:?}",
62 *entity_id
63 ));
64 1
65 }));
66
67 let whereareyou = |ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let position = source.bot.position();
70 source.reply(format!(
71 "I'm at {}, {}, {}",
72 position.x, position.y, position.z
73 ));
74 1
75 };
76 commands.register(literal("whereareyou").executes(whereareyou));
77 commands.register(literal("pos").executes(whereareyou));
78
79 commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
80 let source = ctx.source.lock();
81 source.reply(format!(
82 "I am {} ({}, {})",
83 source.bot.username(),
84 source.bot.uuid(),
85 source.bot.entity
86 ));
87 1
88 }));
89
90 commands.register(literal("getdirection").executes(|ctx: &Ctx| {
91 let source = ctx.source.lock();
92 let direction = source.bot.direction();
93 source.reply(format!(
94 "I'm looking at {}, {}",
95 direction.y_rot(),
96 direction.x_rot()
97 ));
98 1
99 }));
100
101 commands.register(literal("health").executes(|ctx: &Ctx| {
102 let source = ctx.source.lock();
103
104 let health = source.bot.health();
105 source.reply(format!("I have {health} health"));
106 1
107 }));
108
109 commands.register(literal("lookingat").executes(|ctx: &Ctx| {
110 let source = ctx.source.lock();
111
112 let hit_result = source.bot.hit_result();
113
114 match &hit_result {
115 HitResult::Block(r) => {
116 if r.miss {
117 source.reply("I'm not looking at anything");
118 return 0;
119 }
120 let block_pos = r.block_pos;
121 let block = source.bot.world().read().get_block_state(block_pos);
122 source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
123 }
124 HitResult::Entity(r) => {
125 let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
126 source.reply(format!(
127 "I'm looking at {entity_kind} ({:?}) at {}",
128 r.entity, r.location
129 ));
130 }
131 }
132
133 1
134 }));
135
136 commands.register(literal("getblock").then(argument("x", integer()).then(
137 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
138 let source = ctx.source.lock();
139 let x = get_integer(ctx, "x").unwrap();
140 let y = get_integer(ctx, "y").unwrap();
141 let z = get_integer(ctx, "z").unwrap();
142 println!("getblock xyz {x} {y} {z}");
143 let block_pos = BlockPos::new(x, y, z);
144 let block = source.bot.world().read().get_block_state(block_pos);
145 source.reply(format!("BlockKind at {block_pos} is {block:?}"));
146 1
147 })),
148 )));
149 commands.register(literal("getfluid").then(argument("x", integer()).then(
150 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
151 let source = ctx.source.lock();
152 let x = get_integer(ctx, "x").unwrap();
153 let y = get_integer(ctx, "y").unwrap();
154 let z = get_integer(ctx, "z").unwrap();
155 println!("getfluid xyz {x} {y} {z}");
156 let block_pos = BlockPos::new(x, y, z);
157 let block = source.bot.world().read().get_fluid_state(block_pos);
158 source.reply(format!("Fluid at {block_pos} is {block:?}"));
159 1
160 })),
161 )));
162
163 commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
164 let source = ctx.source.lock();
165 let pathfinder = source.bot.get_component::<Pathfinder>();
166 let Some(pathfinder) = pathfinder else {
167 source.reply("I don't have the Pathfinder component");
168 return 1;
169 };
170 source.reply(format!(
171 "pathfinder.is_calculating: {}",
172 pathfinder.is_calculating
173 ));
174
175 let executing_path = source.bot.get_component::<ExecutingPath>();
176 let Some(executing_path) = executing_path else {
177 source.reply("I'm not executing a path");
178 return 1;
179 };
180 source.reply(format!(
181 "is_path_partial: {}, path.len: {}, queued_path.len: {}",
182 executing_path.is_path_partial,
183 executing_path.path.len(),
184 if let Some(queued) = &executing_path.queued_path {
185 queued.len().to_string()
186 } else {
187 "n/a".to_owned()
188 },
189 ));
190 1
191 }));
192 commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
193 let source = ctx.source.lock();
194
195 let Some(entity) = source.entity() else {
196 source.reply("You aren't in render distance!");
197 return 0;
198 };
199 let position = entity.position();
200 let position = BlockPos::from(position);
201
202 let mut edges = Vec::new();
203 let cached_world = CachedWorld::new(source.bot.world(), position);
204 let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
205 let custom_state = CustomPathfinderStateRef::default();
206
207 azalea::pathfinder::moves::default_move(
208 &mut MovesCtx {
209 edges: &mut edges,
210 world: &cached_world,
211 mining_cache: &mining_cache,
212 custom_state: &custom_state,
213 },
214 RelBlockPos::from_origin(position, position),
215 );
216
217 if edges.is_empty() {
218 source.reply("No possible moves.");
219 } else {
220 source.reply("Moves:");
221 for (i, edge) in edges.iter().enumerate() {
222 source.reply(format!("{}) {edge:?}", i + 1));
223 }
224 }
225
226 1
227 }));
228
229 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
230 let source = ctx.source.lock();
231 source.bot.start_use_item();
232 source.reply("Ok!");
233 1
234 }));
235 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 let max_stack_size = source
238 .bot
239 .get_held_item()
240 .get_component::<MaxStackSize>()
241 .map_or(-1, |s| s.count);
242 source.reply(format!("{max_stack_size}"));
243 1
244 }));
245
246 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
247 let source = ctx.source.lock();
248 let bot_dimensions = source.bot.dimensions();
249 source.reply(format!("{bot_dimensions:?}"));
250 1
251 }));
252
253 commands.register(literal("players").executes(|ctx: &Ctx| {
254 let source = ctx.source.lock();
255 let player_entities = source
256 .bot
257 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
258 let tab_list = source.bot.tab_list();
259 for player_entity in player_entities {
260 let uuid = player_entity.uuid();
261 source.reply(format!(
262 "{} - {} ({:?})",
263 player_entity.id(),
264 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
265 uuid
266 ));
267 }
268 1
269 }));
270
271 commands.register(literal("enchants").executes(|ctx: &Ctx| {
272 let source = ctx.source.lock();
273 source.bot.with_registry_holder(|r| {
274 let enchants = &r.enchantment;
275 println!("enchants: {enchants:?}");
276 });
277 1
278 }));
279
280 commands.register(literal("attributes").executes(|ctx: &Ctx| {
281 let source = ctx.source.lock();
282 let attributes = source.bot.attributes();
283 println!("attributes: {attributes:?}");
284 1
285 }));
286
287 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
288 let source = ctx.source.lock();
289
290 source.reply("Ok!");
291
292
293
294 source.bot.disconnect();
295
296 let ecs = source.bot.ecs.clone();
297 thread::spawn(move || {
298 thread::sleep(Duration::from_secs(1));
299 // dump the ecs
300
301 let mut ecs = ecs.write();
302
303 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
304 let mut report = File::create(&report_path).unwrap();
305
306 let mut query = ecs.query::<EntityRef>();
307 for entity in query.iter(& ecs) {
308 writeln!(report, "Entity: {}", entity.id()).unwrap();
309 let archetype = entity.archetype();
310 let component_count = archetype.component_count();
311
312 let component_names = archetype
313 .components()
314 .iter()
315 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
316 .collect::<Vec<_>>();
317 writeln!(
318 report,
319 "- {component_count} components: {}",
320 component_names.join(", ")
321 )
322 .unwrap();
323 }
324
325 writeln!(report).unwrap();
326
327
328 for (info, _) in ecs.iter_resources() {
329 let name = info.name().to_string();
330 writeln!(report, "Resource: {name}").unwrap();
331 // writeln!(report, "- Size: {} bytes",
332 // info.layout().size()).unwrap();
333
334 match name.as_ref() {
335 "azalea_world::container::Worlds" => {
336 let worlds = ecs.resource::<Worlds>();
337
338 for (world_name, world) in &worlds.map {
339 writeln!(report, "- Name: {world_name}").unwrap();
340 writeln!(report, "- Reference count: {}", world.strong_count())
341 .unwrap();
342 if let Some(world) = world.upgrade() {
343 let world = world.read();
344 let strong_chunks = world
345 .chunks
346 .map
347 .iter()
348 .filter(|(_, v)| v.strong_count() > 0)
349 .count();
350 writeln!(
351 report,
352 "- Chunks: {} strongly referenced, {} in map",
353 strong_chunks,
354 world.chunks.map.len()
355 )
356 .unwrap();
357 writeln!(
358 report,
359 "- Entities: {}",
360 world.entities_by_chunk.len()
361 )
362 .unwrap();
363 }
364 }
365 }
366 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
367 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
368 writeln!(report, "- Event count: {}", events.len()).unwrap();
369 }
370 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
371 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
372 writeln!(report, "- Event count: {}", events.len()).unwrap();
373 }
374
375 _ => {}
376 }
377 }
378
379 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
380 });
381
382 1
383 }));
384
385 commands.register(literal("exit").executes(|ctx: &Ctx| {
386 let source = ctx.source.lock();
387 source.reply("bye!");
388
389 source.bot.disconnect();
390
391 let source = ctx.source.clone();
392 thread::spawn(move || {
393 thread::sleep(Duration::from_secs(1));
394
395 source
396 .lock()
397 .bot
398 .ecs
399 .write()
400 .write_message(AppExit::Success);
401 });
402
403 1
404 }));
405}Source§impl EntityRef
impl EntityRef
Sourcepub fn attributes(&self) -> Attributes
pub fn attributes(&self) -> Attributes
Returns the attribute values of the entity, which can be used to determine things like its movement speed.
Source§impl EntityRef
impl EntityRef
pub fn instance_name(&self) -> WorldName
world_name.Source§impl EntityRef
impl EntityRef
Sourcepub fn world_name(&self) -> WorldName
pub fn world_name(&self) -> WorldName
Get the name of the world that the entity is in.
This can be used to check if the entity is in the same world as another entity.
Also see Client::world_name,
Source§impl EntityRef
impl EntityRef
Sourcepub fn is_alive(&self) -> bool
pub fn is_alive(&self) -> bool
Returns whether the entity is alive and hasn’t despawned.
Unlike most functions in EntityRef, this one will not panic if the
entity is despawned. Because of this, it may be useful to check is_alive
before calling functions that request data from the world.
Also see Client::is_alive and Self::exists.
Source§impl EntityRef
impl EntityRef
Sourcepub fn exists(&self) -> bool
pub fn exists(&self) -> bool
Returns whether the entity is in the world and hasn’t despawned.
Like Self::is_alive, this will not panic.
Also see Client::exists.
Source§impl EntityRef
impl EntityRef
Sourcepub fn physics(&self) -> Physics
pub fn physics(&self) -> Physics
Returns the complete Physics data for this entity, including velocity, bounding box,
collisions, etc.
Also see Client::physics.
Source§impl EntityRef
impl EntityRef
pub fn new(client: Client, entity: Entity) -> Self
Sourcepub fn id(&self) -> Entity
pub fn id(&self) -> Entity
Returns the ECS identifier for the entity.
Examples found in repository?
26pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
27 commands.register(literal("ping").executes(|ctx: &Ctx| {
28 let source = ctx.source.lock();
29 source.reply("pong!");
30 1
31 }));
32
33 commands.register(literal("disconnect").executes(|ctx: &Ctx| {
34 let source = ctx.source.lock();
35 source.bot.disconnect();
36 1
37 }));
38
39 commands.register(literal("whereami").executes(|ctx: &Ctx| {
40 let source = ctx.source.lock();
41 let Some(entity) = source.entity() else {
42 source.reply("You aren't in render distance!");
43 return 0;
44 };
45 let position = entity.position();
46 source.reply(format!(
47 "You are at {}, {}, {}",
48 position.x, position.y, position.z
49 ));
50 1
51 }));
52
53 commands.register(literal("entityid").executes(|ctx: &Ctx| {
54 let source = ctx.source.lock();
55 let Some(entity) = source.entity() else {
56 source.reply("You aren't in render distance!");
57 return 0;
58 };
59 let entity_id = entity.minecraft_id();
60 source.reply(format!(
61 "Your Minecraft ID is {} and your ECS ID is {entity:?}",
62 *entity_id
63 ));
64 1
65 }));
66
67 let whereareyou = |ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let position = source.bot.position();
70 source.reply(format!(
71 "I'm at {}, {}, {}",
72 position.x, position.y, position.z
73 ));
74 1
75 };
76 commands.register(literal("whereareyou").executes(whereareyou));
77 commands.register(literal("pos").executes(whereareyou));
78
79 commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
80 let source = ctx.source.lock();
81 source.reply(format!(
82 "I am {} ({}, {})",
83 source.bot.username(),
84 source.bot.uuid(),
85 source.bot.entity
86 ));
87 1
88 }));
89
90 commands.register(literal("getdirection").executes(|ctx: &Ctx| {
91 let source = ctx.source.lock();
92 let direction = source.bot.direction();
93 source.reply(format!(
94 "I'm looking at {}, {}",
95 direction.y_rot(),
96 direction.x_rot()
97 ));
98 1
99 }));
100
101 commands.register(literal("health").executes(|ctx: &Ctx| {
102 let source = ctx.source.lock();
103
104 let health = source.bot.health();
105 source.reply(format!("I have {health} health"));
106 1
107 }));
108
109 commands.register(literal("lookingat").executes(|ctx: &Ctx| {
110 let source = ctx.source.lock();
111
112 let hit_result = source.bot.hit_result();
113
114 match &hit_result {
115 HitResult::Block(r) => {
116 if r.miss {
117 source.reply("I'm not looking at anything");
118 return 0;
119 }
120 let block_pos = r.block_pos;
121 let block = source.bot.world().read().get_block_state(block_pos);
122 source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
123 }
124 HitResult::Entity(r) => {
125 let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
126 source.reply(format!(
127 "I'm looking at {entity_kind} ({:?}) at {}",
128 r.entity, r.location
129 ));
130 }
131 }
132
133 1
134 }));
135
136 commands.register(literal("getblock").then(argument("x", integer()).then(
137 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
138 let source = ctx.source.lock();
139 let x = get_integer(ctx, "x").unwrap();
140 let y = get_integer(ctx, "y").unwrap();
141 let z = get_integer(ctx, "z").unwrap();
142 println!("getblock xyz {x} {y} {z}");
143 let block_pos = BlockPos::new(x, y, z);
144 let block = source.bot.world().read().get_block_state(block_pos);
145 source.reply(format!("BlockKind at {block_pos} is {block:?}"));
146 1
147 })),
148 )));
149 commands.register(literal("getfluid").then(argument("x", integer()).then(
150 argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
151 let source = ctx.source.lock();
152 let x = get_integer(ctx, "x").unwrap();
153 let y = get_integer(ctx, "y").unwrap();
154 let z = get_integer(ctx, "z").unwrap();
155 println!("getfluid xyz {x} {y} {z}");
156 let block_pos = BlockPos::new(x, y, z);
157 let block = source.bot.world().read().get_fluid_state(block_pos);
158 source.reply(format!("Fluid at {block_pos} is {block:?}"));
159 1
160 })),
161 )));
162
163 commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
164 let source = ctx.source.lock();
165 let pathfinder = source.bot.get_component::<Pathfinder>();
166 let Some(pathfinder) = pathfinder else {
167 source.reply("I don't have the Pathfinder component");
168 return 1;
169 };
170 source.reply(format!(
171 "pathfinder.is_calculating: {}",
172 pathfinder.is_calculating
173 ));
174
175 let executing_path = source.bot.get_component::<ExecutingPath>();
176 let Some(executing_path) = executing_path else {
177 source.reply("I'm not executing a path");
178 return 1;
179 };
180 source.reply(format!(
181 "is_path_partial: {}, path.len: {}, queued_path.len: {}",
182 executing_path.is_path_partial,
183 executing_path.path.len(),
184 if let Some(queued) = &executing_path.queued_path {
185 queued.len().to_string()
186 } else {
187 "n/a".to_owned()
188 },
189 ));
190 1
191 }));
192 commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
193 let source = ctx.source.lock();
194
195 let Some(entity) = source.entity() else {
196 source.reply("You aren't in render distance!");
197 return 0;
198 };
199 let position = entity.position();
200 let position = BlockPos::from(position);
201
202 let mut edges = Vec::new();
203 let cached_world = CachedWorld::new(source.bot.world(), position);
204 let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
205 let custom_state = CustomPathfinderStateRef::default();
206
207 azalea::pathfinder::moves::default_move(
208 &mut MovesCtx {
209 edges: &mut edges,
210 world: &cached_world,
211 mining_cache: &mining_cache,
212 custom_state: &custom_state,
213 },
214 RelBlockPos::from_origin(position, position),
215 );
216
217 if edges.is_empty() {
218 source.reply("No possible moves.");
219 } else {
220 source.reply("Moves:");
221 for (i, edge) in edges.iter().enumerate() {
222 source.reply(format!("{}) {edge:?}", i + 1));
223 }
224 }
225
226 1
227 }));
228
229 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
230 let source = ctx.source.lock();
231 source.bot.start_use_item();
232 source.reply("Ok!");
233 1
234 }));
235 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 let max_stack_size = source
238 .bot
239 .get_held_item()
240 .get_component::<MaxStackSize>()
241 .map_or(-1, |s| s.count);
242 source.reply(format!("{max_stack_size}"));
243 1
244 }));
245
246 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
247 let source = ctx.source.lock();
248 let bot_dimensions = source.bot.dimensions();
249 source.reply(format!("{bot_dimensions:?}"));
250 1
251 }));
252
253 commands.register(literal("players").executes(|ctx: &Ctx| {
254 let source = ctx.source.lock();
255 let player_entities = source
256 .bot
257 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
258 let tab_list = source.bot.tab_list();
259 for player_entity in player_entities {
260 let uuid = player_entity.uuid();
261 source.reply(format!(
262 "{} - {} ({:?})",
263 player_entity.id(),
264 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
265 uuid
266 ));
267 }
268 1
269 }));
270
271 commands.register(literal("enchants").executes(|ctx: &Ctx| {
272 let source = ctx.source.lock();
273 source.bot.with_registry_holder(|r| {
274 let enchants = &r.enchantment;
275 println!("enchants: {enchants:?}");
276 });
277 1
278 }));
279
280 commands.register(literal("attributes").executes(|ctx: &Ctx| {
281 let source = ctx.source.lock();
282 let attributes = source.bot.attributes();
283 println!("attributes: {attributes:?}");
284 1
285 }));
286
287 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
288 let source = ctx.source.lock();
289
290 source.reply("Ok!");
291
292
293
294 source.bot.disconnect();
295
296 let ecs = source.bot.ecs.clone();
297 thread::spawn(move || {
298 thread::sleep(Duration::from_secs(1));
299 // dump the ecs
300
301 let mut ecs = ecs.write();
302
303 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
304 let mut report = File::create(&report_path).unwrap();
305
306 let mut query = ecs.query::<EntityRef>();
307 for entity in query.iter(& ecs) {
308 writeln!(report, "Entity: {}", entity.id()).unwrap();
309 let archetype = entity.archetype();
310 let component_count = archetype.component_count();
311
312 let component_names = archetype
313 .components()
314 .iter()
315 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
316 .collect::<Vec<_>>();
317 writeln!(
318 report,
319 "- {component_count} components: {}",
320 component_names.join(", ")
321 )
322 .unwrap();
323 }
324
325 writeln!(report).unwrap();
326
327
328 for (info, _) in ecs.iter_resources() {
329 let name = info.name().to_string();
330 writeln!(report, "Resource: {name}").unwrap();
331 // writeln!(report, "- Size: {} bytes",
332 // info.layout().size()).unwrap();
333
334 match name.as_ref() {
335 "azalea_world::container::Worlds" => {
336 let worlds = ecs.resource::<Worlds>();
337
338 for (world_name, world) in &worlds.map {
339 writeln!(report, "- Name: {world_name}").unwrap();
340 writeln!(report, "- Reference count: {}", world.strong_count())
341 .unwrap();
342 if let Some(world) = world.upgrade() {
343 let world = world.read();
344 let strong_chunks = world
345 .chunks
346 .map
347 .iter()
348 .filter(|(_, v)| v.strong_count() > 0)
349 .count();
350 writeln!(
351 report,
352 "- Chunks: {} strongly referenced, {} in map",
353 strong_chunks,
354 world.chunks.map.len()
355 )
356 .unwrap();
357 writeln!(
358 report,
359 "- Entities: {}",
360 world.entities_by_chunk.len()
361 )
362 .unwrap();
363 }
364 }
365 }
366 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
367 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
368 writeln!(report, "- Event count: {}", events.len()).unwrap();
369 }
370 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
371 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
372 writeln!(report, "- Event count: {}", events.len()).unwrap();
373 }
374
375 _ => {}
376 }
377 }
378
379 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
380 });
381
382 1
383 }));
384
385 commands.register(literal("exit").executes(|ctx: &Ctx| {
386 let source = ctx.source.lock();
387 source.reply("bye!");
388
389 source.bot.disconnect();
390
391 let source = ctx.source.clone();
392 thread::spawn(move || {
393 thread::sleep(Duration::from_secs(1));
394
395 source
396 .lock()
397 .bot
398 .ecs
399 .write()
400 .write_message(AppExit::Success);
401 });
402
403 1
404 }));
405}Sourcepub fn component<T: Component>(&self) -> MappedRwLockReadGuard<'_, T>
pub fn component<T: Component>(&self) -> MappedRwLockReadGuard<'_, T>
Get a component on the entity.
This allows you to access certain data stored about the entity that isn’t accessible in a simpler way.
See Client::component for more details.
§Panics
This will panic if the component doesn’t exist on the client. Use
Self::get_component to avoid this.
§Examples
let world_name = client.component::<WorldName>();Sourcepub fn get_component<T: Component>(
&self,
) -> Option<MappedRwLockReadGuard<'_, T>>
pub fn get_component<T: Component>( &self, ) -> Option<MappedRwLockReadGuard<'_, T>>
Get a component on this client, or None if it doesn’t exist.
If the component is guaranteed to be present, consider using
Self::component.
See Client::component for more details.
Sourcepub fn query_self<D: QueryData, R>(
&self,
f: impl FnOnce(QueryItem<'_, '_, D>) -> R,
) -> R
pub fn query_self<D: QueryData, R>( &self, f: impl FnOnce(QueryItem<'_, '_, D>) -> R, ) -> R
Query the ECS for data from the entity.
You can use this to mutate data on the entity.
Also see Client::query_self and Client::query_entity.
§Panics
This will panic if the entity doesn’t exist or is missing a component
required by the query. Consider using Self::try_query_self to
avoid this.
Sourcepub fn try_query_self<D: QueryData, R>(
&self,
f: impl FnOnce(QueryItem<'_, '_, D>) -> R,
) -> Result<R, QueryEntityError>
pub fn try_query_self<D: QueryData, R>( &self, f: impl FnOnce(QueryItem<'_, '_, D>) -> R, ) -> Result<R, QueryEntityError>
Query the ECS for data from the entity, or return an error if the query fails.
Also see Self::query_self.
Source§impl EntityRef
impl EntityRef
Sourcepub fn attack(&self)
pub fn attack(&self)
Attack this entity from the client that created this EntityRef.
Also see Client::attack.
Examples found in repository?
9pub fn tick(bot: Client, state: State) -> anyhow::Result<()> {
10 if !state.killaura {
11 return Ok(());
12 }
13 if bot.has_attack_cooldown() {
14 return Ok(());
15 }
16 let bot_position = bot.eye_position();
17
18 let nearest_entity = bot.nearest_entity_by::<&Position, (
19 With<AbstractMonster>,
20 Without<LocalEntity>,
21 Without<Dead>,
22 )>(|position: &Position| {
23 let distance = bot_position.distance_to(**position);
24 distance < 4.
25 });
26
27 if let Some(nearest_entity) = nearest_entity {
28 println!("attacking {nearest_entity:?}");
29 nearest_entity.attack();
30 }
31
32 Ok(())
33}Sourcepub fn interact(&self)
pub fn interact(&self)
Right-click this entity from the client that created this EntityRef.
See Client::entity_interact for more information.
Sourcepub fn distance_to_client(&self) -> f64
pub fn distance_to_client(&self) -> f64
Returns the distance between the client’s feet position and this entity’s feet position.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for EntityRef
impl !RefUnwindSafe for EntityRef
impl Send for EntityRef
impl Sync for EntityRef
impl Unpin for EntityRef
impl !UnwindSafe for EntityRef
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> CompatExt for T
impl<T> CompatExt for T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.