pub struct Client {
pub entity: Entity,
pub ecs: Arc<Mutex<RawMutex, World>>,
}Expand description
A Minecraft client instance that can interact with the world.
To make a new client, use either azalea::ClientBuilder or
Client::join.
Note that Client is inaccessible from systems (i.e. plugins), but you can
achieve everything that client can do with ECS events.
Fields§
§entity: EntityThe entity for this client in the ECS.
ecs: Arc<Mutex<RawMutex, World>>A mutually exclusive reference to the entity component system (ECS).
You probably don’t need to access this directly. Note that if you’re using a shared world (i.e. a swarm), the ECS will contain all entities in all instances/dimensions.
Implementations§
Source§impl Client
impl Client
Sourcepub fn new(entity: Entity, ecs: Arc<Mutex<RawMutex, World>>) -> Client
pub fn new(entity: Entity, ecs: Arc<Mutex<RawMutex, World>>) -> Client
Create a new client from the given GameProfile, ECS Entity, ECS
World, and schedule runner function.
You should only use this if you want to change these fields from the
defaults, otherwise use Client::join.
Sourcepub async fn join(
account: Account,
address: impl ResolvableAddr,
) -> Result<(Client, UnboundedReceiver<Event>), ResolveError>
pub async fn join( account: Account, address: impl ResolvableAddr, ) -> Result<(Client, UnboundedReceiver<Event>), ResolveError>
Connect to a Minecraft server.
To change the render distance and other settings, use
Client::set_client_information. To watch for events like packets
sent by the server, use the rx variable this function returns.
§Examples
use azalea_client::{Account, Client};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let account = Account::offline("bot");
let (client, rx) = Client::join(account, "localhost").await?;
client.chat("Hello, world!");
client.disconnect();
Ok(())
}pub async fn join_with_proxy( account: Account, address: impl ResolvableAddr, proxy: Proxy, ) -> Result<(Client, UnboundedReceiver<Event>), ResolveError>
Sourcepub async fn start_client(__arg0: StartClientOpts) -> Client
pub async fn start_client(__arg0: StartClientOpts) -> Client
Create a Client when you already have the ECS made with
[start_ecs_runner]. You’d usually want to use Self::join instead.
Sourcepub fn write_packet(&self, packet: impl Packet<ServerboundGamePacket>)
pub fn write_packet(&self, packet: impl Packet<ServerboundGamePacket>)
Write a packet directly to the server.
Sourcepub fn disconnect(&self)
pub fn disconnect(&self)
Disconnect this client from the server by ending all tasks.
The OwnedReadHalf for the TCP connection is in one of the tasks, so it automatically closes the connection when that’s dropped.
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}pub fn with_raw_connection<R>(&self, f: impl FnOnce(&RawConnection) -> R) -> R
pub fn with_raw_connection_mut<R>( &self, f: impl FnOnce(Mut<'_, RawConnection>) -> R, ) -> R
Sourcepub fn component<T>(&self) -> T
pub fn component<T>(&self) -> T
Get a component from this client. This will clone the component and return it.
If the component can’t be cloned, try Self::query_self instead.
If it isn’t guaranteed to be present, you can use
Self::get_component or Self::query_self.
You may also use Self::ecs directly if you need more control over
when the ECS is locked.
§Panics
This will panic if the component doesn’t exist on the client.
§Examples
let world_name = client.component::<InstanceName>();Examples found in repository?
10pub fn tick(bot: Client, state: State) -> anyhow::Result<()> {
11 if !state.killaura {
12 return Ok(());
13 }
14 if bot.has_attack_cooldown() {
15 return Ok(());
16 }
17 let mut nearest_entity = None;
18 let mut nearest_distance = f64::INFINITY;
19 let bot_position = bot.eye_position();
20 let bot_instance_name = bot.component::<InstanceName>();
21 {
22 let mut ecs = bot.ecs.lock();
23 let mut query = ecs
24 .query_filtered::<(Entity, &Position, &InstanceName), (
25 With<AbstractMonster>,
26 Without<LocalEntity>,
27 Without<Dead>,
28 )>();
29 for (entity_id, position, instance_name) in query.iter(&ecs) {
30 if instance_name != &bot_instance_name {
31 continue;
32 }
33
34 let distance = bot_position.distance_to(**position);
35 if distance < 4. && distance < nearest_distance {
36 nearest_entity = Some(entity_id);
37 nearest_distance = distance;
38 }
39 }
40 }
41 if let Some(nearest_entity) = nearest_entity {
42 println!("attacking {nearest_entity:?}");
43 println!("distance {nearest_distance:?}");
44 bot.attack(nearest_entity);
45 }
46
47 Ok(())
48}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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn get_component<T>(&self) -> Option<T>
pub fn get_component<T>(&self) -> Option<T>
Get a component from this client, or None if it doesn’t exist.
If the component can’t be cloned, consider using Self::query_self
with Option<&T> instead.
You may also have to use Self::query_self directly.
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn resource<T>(&self) -> T
pub fn resource<T>(&self) -> T
Get a resource from the ECS. This will clone the resource and return it.
Examples found in repository?
121async fn handle(bot: Client, event: azalea::Event, state: State) -> anyhow::Result<()> {
122 let swarm = bot.resource::<SwarmState>();
123
124 match event {
125 azalea::Event::Init => {
126 bot.set_client_information(ClientInformation {
127 view_distance: 32,
128 ..Default::default()
129 });
130 if swarm.args.pathfinder_debug_particles {
131 bot.ecs
132 .lock()
133 .entity_mut(bot.entity)
134 .insert(PathfinderDebugParticles);
135 }
136 }
137 azalea::Event::Chat(chat) => {
138 let (Some(username), content) = chat.split_sender_and_content() else {
139 return Ok(());
140 };
141 if username != swarm.args.owner_username {
142 return Ok(());
143 }
144
145 println!("{:?}", chat.message());
146
147 let command = if chat.is_whisper() {
148 Some(content)
149 } else {
150 content.strip_prefix('!').map(|s| s.to_owned())
151 };
152 if let Some(command) = command {
153 match swarm.commands.execute(
154 command,
155 Mutex::new(CommandSource {
156 bot: bot.clone(),
157 chat: chat.clone(),
158 state: state.clone(),
159 }),
160 ) {
161 Ok(_) => {}
162 Err(err) => {
163 eprintln!("{err:?}");
164 let command_source = CommandSource {
165 bot,
166 chat: chat.clone(),
167 state: state.clone(),
168 };
169 command_source.reply(format!("{err:?}"));
170 }
171 }
172 }
173 }
174 azalea::Event::Tick => {
175 killaura::tick(bot.clone(), state.clone())?;
176
177 let task = *state.task.lock();
178 match task {
179 BotTask::None => {}
180 }
181 }
182 azalea::Event::Login => {
183 println!("Got login event")
184 }
185 _ => {}
186 }
187
188 Ok(())
189}Sourcepub fn map_resource<T, R>(&self, f: impl FnOnce(&T) -> R) -> Rwhere
T: Resource,
pub fn map_resource<T, R>(&self, f: impl FnOnce(&T) -> R) -> Rwhere
T: Resource,
Get a required ECS resource and call the given function with it.
Sourcepub fn map_get_resource<T, R>(&self, f: impl FnOnce(Option<&T>) -> R) -> Rwhere
T: Resource,
pub fn map_get_resource<T, R>(&self, f: impl FnOnce(Option<&T>) -> R) -> Rwhere
T: Resource,
Get an optional ECS resource and call the given function with it.
Sourcepub fn world(&self) -> Arc<RwLock<RawRwLock, Instance>>
pub fn world(&self) -> Arc<RwLock<RawRwLock, Instance>>
Get an RwLock with a reference to our (potentially shared) world.
This gets the Instance from the client’s InstanceHolder
component. If it’s a normal client, then it’ll be the same as the
world the client has loaded. If the client is using a shared world,
then the shared world will be a superset of the client’s world.
Examples found in repository?
42async fn steal(bot: Client, state: State) -> anyhow::Result<()> {
43 {
44 let mut is_stealing = state.is_stealing.lock();
45 if *is_stealing {
46 bot.chat("Already stealing");
47 return Ok(());
48 }
49 *is_stealing = true;
50 }
51
52 state.checked_chests.lock().clear();
53
54 loop {
55 let chest_block = bot
56 .world()
57 .read()
58 .find_blocks(bot.position(), &BlockKind::Chest.into())
59 .find(
60 // find the closest chest that hasn't been checked
61 |block_pos| !state.checked_chests.lock().contains(block_pos),
62 );
63 let Some(chest_block) = chest_block else {
64 break;
65 };
66
67 state.checked_chests.lock().push(chest_block);
68
69 bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
70
71 let Some(chest) = bot.open_container_at(chest_block).await else {
72 println!("Couldn't open chest at {chest_block:?}");
73 continue;
74 };
75
76 println!("Getting contents of chest at {chest_block:?}");
77 for (index, slot) in chest.contents().unwrap_or_default().iter().enumerate() {
78 println!("Checking slot {index}: {slot:?}");
79 let ItemStack::Present(item) = slot else {
80 continue;
81 };
82 if item.kind == ItemKind::Diamond {
83 println!("clicking slot ^");
84 chest.click(QuickMoveClick::Left { slot: index as u16 });
85 }
86 }
87 }
88
89 bot.chat("Done");
90
91 *state.is_stealing.lock() = false;
92
93 Ok(())
94}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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn partial_world(&self) -> Arc<RwLock<RawRwLock, PartialInstance>>
pub fn partial_world(&self) -> Arc<RwLock<RawRwLock, PartialInstance>>
Get an RwLock with a reference to the world that this client has
loaded.
let world = client.partial_world();
let is_0_0_loaded = world.read().chunks.limited_get(&ChunkPos::new(0, 0)).is_some();Source§impl Client
impl Client
Sourcepub fn position(&self) -> Vec3
pub fn position(&self) -> Vec3
Get the position of this client.
This is a shortcut for Vec3::from(&bot.component::<Position>()).
Note that this value is given a default of Vec3::ZERO when it
receives the login packet, its true position may be set ticks
later.
Examples found in repository?
42async fn steal(bot: Client, state: State) -> anyhow::Result<()> {
43 {
44 let mut is_stealing = state.is_stealing.lock();
45 if *is_stealing {
46 bot.chat("Already stealing");
47 return Ok(());
48 }
49 *is_stealing = true;
50 }
51
52 state.checked_chests.lock().clear();
53
54 loop {
55 let chest_block = bot
56 .world()
57 .read()
58 .find_blocks(bot.position(), &BlockKind::Chest.into())
59 .find(
60 // find the closest chest that hasn't been checked
61 |block_pos| !state.checked_chests.lock().contains(block_pos),
62 );
63 let Some(chest_block) = chest_block else {
64 break;
65 };
66
67 state.checked_chests.lock().push(chest_block);
68
69 bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
70
71 let Some(chest) = bot.open_container_at(chest_block).await else {
72 println!("Couldn't open chest at {chest_block:?}");
73 continue;
74 };
75
76 println!("Getting contents of chest at {chest_block:?}");
77 for (index, slot) in chest.contents().unwrap_or_default().iter().enumerate() {
78 println!("Checking slot {index}: {slot:?}");
79 let ItemStack::Present(item) = slot else {
80 continue;
81 };
82 if item.kind == ItemKind::Diamond {
83 println!("clicking slot ^");
84 chest.click(QuickMoveClick::Left { slot: index as u16 });
85 }
86 }
87 }
88
89 bot.chat("Done");
90
91 *state.is_stealing.lock() = false;
92
93 Ok(())
94}More examples
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn dimensions(&self) -> EntityDimensions
pub fn dimensions(&self) -> EntityDimensions
Get the bounding box dimensions for our client, which contains our width, height, and eye height.
This is a shortcut for
self.component::<EntityDimensions>().
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn eye_position(&self) -> Vec3
pub fn eye_position(&self) -> Vec3
Get the position of this client’s eyes.
This is a shortcut for
bot.position().up(bot.dimensions().eye_height).
Examples found in repository?
10pub fn tick(bot: Client, state: State) -> anyhow::Result<()> {
11 if !state.killaura {
12 return Ok(());
13 }
14 if bot.has_attack_cooldown() {
15 return Ok(());
16 }
17 let mut nearest_entity = None;
18 let mut nearest_distance = f64::INFINITY;
19 let bot_position = bot.eye_position();
20 let bot_instance_name = bot.component::<InstanceName>();
21 {
22 let mut ecs = bot.ecs.lock();
23 let mut query = ecs
24 .query_filtered::<(Entity, &Position, &InstanceName), (
25 With<AbstractMonster>,
26 Without<LocalEntity>,
27 Without<Dead>,
28 )>();
29 for (entity_id, position, instance_name) in query.iter(&ecs) {
30 if instance_name != &bot_instance_name {
31 continue;
32 }
33
34 let distance = bot_position.distance_to(**position);
35 if distance < 4. && distance < nearest_distance {
36 nearest_entity = Some(entity_id);
37 nearest_distance = distance;
38 }
39 }
40 }
41 if let Some(nearest_entity) = nearest_entity {
42 println!("attacking {nearest_entity:?}");
43 println!("distance {nearest_distance:?}");
44 bot.attack(nearest_entity);
45 }
46
47 Ok(())
48}Sourcepub fn health(&self) -> f32
pub fn health(&self) -> f32
Get the health of this client.
This is a shortcut for *bot.component::<Health>().
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn hunger(&self) -> Hunger
pub fn hunger(&self) -> Hunger
Get the hunger level of this client, which includes both food and saturation.
This is a shortcut for self.component::<Hunger>().to_owned().
Sourcepub fn username(&self) -> String
pub fn username(&self) -> String
Get the username of this client.
This is a shortcut for
bot.component::<GameProfileComponent>().name.to_owned().
Examples found in repository?
27async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
28 if let Event::Chat(m) = event {
29 if m.sender() == Some(bot.username()) {
30 return Ok(());
31 };
32 if m.content() != "go" {
33 return Ok(());
34 }
35
36 steal(bot, state).await?;
37 }
38
39 Ok(())
40}More examples
19async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
20 if let Event::Chat(m) = event
21 && let (Some(sender), content) = m.split_sender_and_content()
22 {
23 if sender == bot.username() {
24 // ignore our own messages
25 return Ok(());
26 }
27 bot.chat(content);
28 }
29
30 Ok(())
31}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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn uuid(&self) -> Uuid
pub fn uuid(&self) -> Uuid
Get the Minecraft UUID of this client.
This is a shortcut for bot.component::<GameProfileComponent>().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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn tab_list(&self) -> HashMap<Uuid, PlayerInfo>
pub fn tab_list(&self) -> HashMap<Uuid, PlayerInfo>
Get a map of player UUIDs to their information in the tab list.
This is a shortcut for *bot.component::<TabList>().
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn profile(&self) -> GameProfile
pub fn profile(&self) -> GameProfile
Returns the GameProfile for our client. This contains your username,
UUID, and skin data.
These values are set by the server upon login, which means they might
not match up with your actual game profile. Also, note that the username
and skin that gets displayed in-game will actually be the ones from
the tab list, which you can get from Self::tab_list.
This as also available from the ECS as GameProfileComponent.
Sourcepub fn attributes(&self) -> Attributes
pub fn attributes(&self) -> Attributes
Returns the attribute values of our player, which can be used to determine things like our movement speed.
Sourcepub fn player_uuid_by_username(&self, username: &str) -> Option<Uuid>
pub fn player_uuid_by_username(&self, username: &str) -> Option<Uuid>
A convenience function to get the Minecraft Uuid of a player by their username, if they’re present in the tab list.
You can chain this with Client::entity_by_uuid to get the ECS
Entity for the player.
Sourcepub fn entity_by_uuid(&self, uuid: Uuid) -> Option<Entity>
pub fn entity_by_uuid(&self, uuid: Uuid) -> Option<Entity>
Get an ECS Entity in the world by its Minecraft UUID, if it’s within
render distance.
Sourcepub fn minecraft_entity_by_ecs_entity(
&self,
entity: Entity,
) -> Option<MinecraftEntityId>
pub fn minecraft_entity_by_ecs_entity( &self, entity: Entity, ) -> Option<MinecraftEntityId>
Convert an ECS Entity to a MinecraftEntityId.
Sourcepub fn ecs_entity_by_minecraft_entity(
&self,
entity: MinecraftEntityId,
) -> Option<Entity>
pub fn ecs_entity_by_minecraft_entity( &self, entity: MinecraftEntityId, ) -> Option<Entity>
Convert a MinecraftEntityId to an ECS Entity.
Sourcepub fn with_registry_holder<R>(&self, f: impl FnOnce(&RegistryHolder) -> R) -> R
pub fn with_registry_holder<R>(&self, f: impl FnOnce(&RegistryHolder) -> R) -> R
Call the given function with the client’s RegistryHolder.
The player’s instance (aka world) will be locked during this time, which may result in a deadlock if you try to access the instance again while in the function.
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn resolve_registry_name(
&self,
registry: &impl ResolvableDataRegistry,
) -> Option<Identifier>
pub fn resolve_registry_name( &self, registry: &impl ResolvableDataRegistry, ) -> Option<Identifier>
Resolve the given registry to its name.
This is necessary for data-driven registries like Enchantment.
Sourcepub fn with_resolved_registry<R, Ret>(
&self,
registry: R,
f: impl FnOnce(&Identifier, &<R as ResolvableDataRegistry>::DeserializesTo) -> Ret,
) -> Option<Ret>where
R: ResolvableDataRegistry,
pub fn with_resolved_registry<R, Ret>(
&self,
registry: R,
f: impl FnOnce(&Identifier, &<R as ResolvableDataRegistry>::DeserializesTo) -> Ret,
) -> Option<Ret>where
R: ResolvableDataRegistry,
Resolve the given registry to its name and data and call the given function with it.
This is necessary for data-driven registries like Enchantment.
If you just want the value name, use Self::resolve_registry_name
instead.
Source§impl Client
impl Client
Sourcepub fn query_self<D, R>(
&self,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Rwhere
D: QueryData,
pub fn query_self<D, R>(
&self,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Rwhere
D: QueryData,
A convenience function for getting components from our client’s entity.
To query another entity, you can use Self::query_entity.
§Examples
let is_logged_in = client.query_self::<Option<&InstanceName>, _>(|ins| ins.is_some());§Panics
This will panic if the component doesn’t exist on the client.
Sourcepub fn query_entity<D, R>(
&self,
entity: Entity,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Rwhere
D: QueryData,
pub fn query_entity<D, R>(
&self,
entity: Entity,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Rwhere
D: QueryData,
A convenience function for getting components from any entity.
If you’re querying the client, you should use Self::query_self.
§Panics
This will panic if the entity doesn’t exist or if the query isn’t valid
for the entity. For a non-panicking version, you may use
Self::try_query_entity.
Sourcepub fn try_query_entity<D, R>(
&self,
entity: Entity,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Result<R, QueryEntityError>where
D: QueryData,
pub fn try_query_entity<D, R>(
&self,
entity: Entity,
f: impl FnOnce(<D as QueryData>::Item<'_, '_>) -> R,
) -> Result<R, QueryEntityError>where
D: QueryData,
A convenience function for getting components from any entity, or None if the query fails.
If you’re sure that the entity exists and that the query will succeed,
you can use Self::query_entity.
Sourcepub fn any_entity_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Option<Entity>where
Q: QueryData,
F: QueryFilter,
pub fn any_entity_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Option<Entity>where
Q: QueryData,
F: QueryFilter,
Quickly returns a lightweight Entity for an arbitrary entity that
matches the given predicate function that is in the same
Instance as the client.
You can then use Self::entity_component to get components from this
entity.
If you want to find the nearest entity, consider using
Self::nearest_entity_by instead. If you want to find all entities
that match the predicate, use Self::nearest_entities_by.
§Example
use azalea_client::{Client, player::GameProfileComponent};
use azalea_entity::{Position, metadata::Player};
use bevy_ecs::query::With;
let entity = bot.any_entity_by::<&GameProfileComponent, With<Player>>(
|profile: &GameProfileComponent| profile.name == sender_name,
);
if let Some(entity) = entity {
let position = bot.entity_component::<Position>(entity);
// ...
}Sourcepub fn nearest_entity_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Option<Entity>where
Q: QueryData,
F: QueryFilter,
pub fn nearest_entity_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Option<Entity>where
Q: QueryData,
F: QueryFilter,
Return a lightweight Entity for the nearest entity that matches the
given predicate function.
You can then use Self::entity_component to get components from this
entity.
If you don’t need the entity to be the nearest one, it may be more
efficient to use Self::any_entity_by instead. You can also use
Self::nearest_entities_by to get all nearby entities.
use azalea_entity::{LocalEntity, Position, metadata::Player};
use bevy_ecs::query::{With, Without};
// get the position of the nearest player
if let Some(nearest_player) =
bot.nearest_entity_by::<(), (With<Player>, Without<LocalEntity>)>(|_: ()| true)
{
let nearest_player_pos = *bot.entity_component::<Position>(nearest_player);
bot.chat(format!("You are at {nearest_player_pos}"));
}Sourcepub fn nearest_entities_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Vec<Entity>where
Q: QueryData,
F: QueryFilter,
pub fn nearest_entities_by<Q, F>(
&self,
predicate: impl EntityPredicate<Q, F>,
) -> Vec<Entity>where
Q: QueryData,
F: QueryFilter,
Similar to Self::nearest_entity_by but returns a Vec<Entity> of
all entities in our instance that match the predicate.
The first entity is the nearest one.
let nearby_players =
bot.nearest_entities_by::<(), (With<Player>, Without<LocalEntity>)>(|_: ()| true);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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn entity_component<Q>(&self, entity: Entity) -> Q
pub fn entity_component<Q>(&self, entity: Entity) -> Q
Get a component from an entity.
Note that this will return an owned type (i.e. not a reference) so it may be expensive for larger types.
If you’re trying to get a component for this client, use
Self::component.
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Sourcepub fn get_entity_component<Q>(&self, entity: Entity) -> Option<Q>
pub fn get_entity_component<Q>(&self, entity: Entity) -> Option<Q>
Get a component from an entity, if it exists.
This is similar to Self::entity_component but returns an Option
instead of panicking if the component isn’t present.
Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Source§impl Client
impl Client
Sourcepub fn attack(&self, entity: Entity)
pub fn attack(&self, entity: Entity)
Attack an entity in the world.
This doesn’t automatically look at the entity or perform any range/visibility checks, so it might trigger anticheats.
Examples found in repository?
10pub fn tick(bot: Client, state: State) -> anyhow::Result<()> {
11 if !state.killaura {
12 return Ok(());
13 }
14 if bot.has_attack_cooldown() {
15 return Ok(());
16 }
17 let mut nearest_entity = None;
18 let mut nearest_distance = f64::INFINITY;
19 let bot_position = bot.eye_position();
20 let bot_instance_name = bot.component::<InstanceName>();
21 {
22 let mut ecs = bot.ecs.lock();
23 let mut query = ecs
24 .query_filtered::<(Entity, &Position, &InstanceName), (
25 With<AbstractMonster>,
26 Without<LocalEntity>,
27 Without<Dead>,
28 )>();
29 for (entity_id, position, instance_name) in query.iter(&ecs) {
30 if instance_name != &bot_instance_name {
31 continue;
32 }
33
34 let distance = bot_position.distance_to(**position);
35 if distance < 4. && distance < nearest_distance {
36 nearest_entity = Some(entity_id);
37 nearest_distance = distance;
38 }
39 }
40 }
41 if let Some(nearest_entity) = nearest_entity {
42 println!("attacking {nearest_entity:?}");
43 println!("distance {nearest_distance:?}");
44 bot.attack(nearest_entity);
45 }
46
47 Ok(())
48}Sourcepub fn has_attack_cooldown(&self) -> bool
pub fn has_attack_cooldown(&self) -> bool
Whether the player has an attack cooldown.
Also see Client::attack_cooldown_remaining_ticks.
Examples found in repository?
10pub fn tick(bot: Client, state: State) -> anyhow::Result<()> {
11 if !state.killaura {
12 return Ok(());
13 }
14 if bot.has_attack_cooldown() {
15 return Ok(());
16 }
17 let mut nearest_entity = None;
18 let mut nearest_distance = f64::INFINITY;
19 let bot_position = bot.eye_position();
20 let bot_instance_name = bot.component::<InstanceName>();
21 {
22 let mut ecs = bot.ecs.lock();
23 let mut query = ecs
24 .query_filtered::<(Entity, &Position, &InstanceName), (
25 With<AbstractMonster>,
26 Without<LocalEntity>,
27 Without<Dead>,
28 )>();
29 for (entity_id, position, instance_name) in query.iter(&ecs) {
30 if instance_name != &bot_instance_name {
31 continue;
32 }
33
34 let distance = bot_position.distance_to(**position);
35 if distance < 4. && distance < nearest_distance {
36 nearest_entity = Some(entity_id);
37 nearest_distance = distance;
38 }
39 }
40 }
41 if let Some(nearest_entity) = nearest_entity {
42 println!("attacking {nearest_entity:?}");
43 println!("distance {nearest_distance:?}");
44 bot.attack(nearest_entity);
45 }
46
47 Ok(())
48}Sourcepub fn attack_cooldown_remaining_ticks(&self) -> usize
pub fn attack_cooldown_remaining_ticks(&self) -> usize
Returns the number of ticks until we can attack at full strength again.
Also see Client::has_attack_cooldown.
Source§impl Client
impl Client
Sourcepub fn write_chat_packet(&self, message: &str)
pub fn write_chat_packet(&self, message: &str)
Send a chat message to the server.
This only sends the chat packet and not the command packet, which means
on some servers you can use this to send chat messages that start
with a /. The Client::chat function handles checking whether
the message is a command and using the proper packet for you, so you
should use that instead.
Sourcepub fn write_command_packet(&self, command: &str)
pub fn write_command_packet(&self, command: &str)
Send a command packet to the server. The command argument should not
include the slash at the front.
You can also just use Client::chat and start your message with a /
to send a command.
Sourcepub fn chat(&self, content: impl Into<String>)
pub fn chat(&self, content: impl Into<String>)
Send a message in chat.
bot.chat("Hello, world!");Examples found in repository?
More examples
19async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
20 if let Event::Chat(m) = event
21 && let (Some(sender), content) = m.split_sender_and_content()
22 {
23 if sender == bot.username() {
24 // ignore our own messages
25 return Ok(());
26 }
27 bot.chat(content);
28 }
29
30 Ok(())
31}42async fn steal(bot: Client, state: State) -> anyhow::Result<()> {
43 {
44 let mut is_stealing = state.is_stealing.lock();
45 if *is_stealing {
46 bot.chat("Already stealing");
47 return Ok(());
48 }
49 *is_stealing = true;
50 }
51
52 state.checked_chests.lock().clear();
53
54 loop {
55 let chest_block = bot
56 .world()
57 .read()
58 .find_blocks(bot.position(), &BlockKind::Chest.into())
59 .find(
60 // find the closest chest that hasn't been checked
61 |block_pos| !state.checked_chests.lock().contains(block_pos),
62 );
63 let Some(chest_block) = chest_block else {
64 break;
65 };
66
67 state.checked_chests.lock().push(chest_block);
68
69 bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
70
71 let Some(chest) = bot.open_container_at(chest_block).await else {
72 println!("Couldn't open chest at {chest_block:?}");
73 continue;
74 };
75
76 println!("Getting contents of chest at {chest_block:?}");
77 for (index, slot) in chest.contents().unwrap_or_default().iter().enumerate() {
78 println!("Checking slot {index}: {slot:?}");
79 let ItemStack::Present(item) = slot else {
80 continue;
81 };
82 if item.kind == ItemKind::Diamond {
83 println!("clicking slot ^");
84 chest.click(QuickMoveClick::Left { slot: index as u16 });
85 }
86 }
87 }
88
89 bot.chat("Done");
90
91 *state.is_stealing.lock() = false;
92
93 Ok(())
94}Source§impl Client
impl Client
Sourcepub fn set_client_information(&self, client_information: ClientInformation)
pub fn set_client_information(&self, client_information: ClientInformation)
Tell the server we changed our game options (i.e. render distance, main hand).
If this is not set before the login packet, the default will be sent.
bot.set_client_information(ClientInformation {
view_distance: 2,
..Default::default()
});Examples found in repository?
121async fn handle(bot: Client, event: azalea::Event, state: State) -> anyhow::Result<()> {
122 let swarm = bot.resource::<SwarmState>();
123
124 match event {
125 azalea::Event::Init => {
126 bot.set_client_information(ClientInformation {
127 view_distance: 32,
128 ..Default::default()
129 });
130 if swarm.args.pathfinder_debug_particles {
131 bot.ecs
132 .lock()
133 .entity_mut(bot.entity)
134 .insert(PathfinderDebugParticles);
135 }
136 }
137 azalea::Event::Chat(chat) => {
138 let (Some(username), content) = chat.split_sender_and_content() else {
139 return Ok(());
140 };
141 if username != swarm.args.owner_username {
142 return Ok(());
143 }
144
145 println!("{:?}", chat.message());
146
147 let command = if chat.is_whisper() {
148 Some(content)
149 } else {
150 content.strip_prefix('!').map(|s| s.to_owned())
151 };
152 if let Some(command) = command {
153 match swarm.commands.execute(
154 command,
155 Mutex::new(CommandSource {
156 bot: bot.clone(),
157 chat: chat.clone(),
158 state: state.clone(),
159 }),
160 ) {
161 Ok(_) => {}
162 Err(err) => {
163 eprintln!("{err:?}");
164 let command_source = CommandSource {
165 bot,
166 chat: chat.clone(),
167 state: state.clone(),
168 };
169 command_source.reply(format!("{err:?}"));
170 }
171 }
172 }
173 }
174 azalea::Event::Tick => {
175 killaura::tick(bot.clone(), state.clone())?;
176
177 let task = *state.task.lock();
178 match task {
179 BotTask::None => {}
180 }
181 }
182 azalea::Event::Login => {
183 println!("Got login event")
184 }
185 _ => {}
186 }
187
188 Ok(())
189}Source§impl Client
impl Client
Sourcepub fn block_interact(&self, position: BlockPos)
pub fn block_interact(&self, position: BlockPos)
Right-click a block.
The behavior of this depends on the target block, and it’ll either place the block you’re holding in your hand or use the block you clicked (like toggling a lever).
Note that this may trigger anticheats as it doesn’t take into account whether you’re actually looking at the block.
Sourcepub fn entity_interact(&self, entity: Entity)
pub fn entity_interact(&self, entity: Entity)
Right-click an entity.
This can click through walls, which may trigger anticheats. If that
behavior isn’t desired, consider using Client::start_use_item
instead.
Sourcepub fn start_use_item(&self)
pub fn start_use_item(&self)
Right-click the currently held item.
If the item is consumable, then it’ll act as if right-click was held until the item finishes being consumed. You can use this to eat food.
If we’re looking at a block or entity, then it will be clicked. Also see
Client::block_interact and Client::entity_interact.
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 mut 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 = source.bot.entity_component::<Position>(entity);
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 mut 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 = source.bot.entity_component::<MinecraftEntityId>(entity);
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.component::<LookDirection>();
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.component::<HitResultComponent>();
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 ocmponent");
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
193 commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
194 let source = ctx.source.lock();
195 source.bot.start_use_item();
196 source.reply("Ok!");
197 1
198 }));
199 commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
200 let source = ctx.source.lock();
201 let max_stack_size = source
202 .bot
203 .get_held_item()
204 .get_component::<MaxStackSize>()
205 .map_or(-1, |s| s.count);
206 source.reply(format!("{max_stack_size}"));
207 1
208 }));
209
210 commands.register(literal("dimensions").executes(|ctx: &Ctx| {
211 let source = ctx.source.lock();
212 let bot_dimensions = source.bot.dimensions();
213 source.reply(format!("{bot_dimensions:?}"));
214 1
215 }));
216
217 commands.register(literal("players").executes(|ctx: &Ctx| {
218 let source = ctx.source.lock();
219 let player_entities = source
220 .bot
221 .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
222 let tab_list = source.bot.tab_list();
223 for player_entity in player_entities {
224 let uuid = source.bot.entity_component::<EntityUuid>(player_entity);
225 source.reply(format!(
226 "{} - {} ({:?})",
227 player_entity,
228 tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
229 uuid
230 ));
231 }
232 1
233 }));
234
235 commands.register(literal("enchants").executes(|ctx: &Ctx| {
236 let source = ctx.source.lock();
237 source.bot.with_registry_holder(|r| {
238 let enchants = &r.enchantment;
239 println!("enchants: {enchants:?}");
240 });
241 1
242 }));
243
244 commands.register(literal("attributes").executes(|ctx: &Ctx| {
245 let source = ctx.source.lock();
246 let attributes = source.bot.component::<Attributes>();
247 println!("attributes: {attributes:?}");
248 1
249 }));
250
251 commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
252 let source = ctx.source.lock();
253
254 source.reply("Ok!");
255
256
257
258 source.bot.disconnect();
259
260 let ecs = source.bot.ecs.clone();
261 thread::spawn(move || {
262 thread::sleep(Duration::from_secs(1));
263 // dump the ecs
264
265 let mut ecs = ecs.lock();
266
267
268
269 let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
270 let mut report = File::create(&report_path).unwrap();
271
272 let mut query = ecs.query::<EntityRef>();
273 for entity in query.iter(& ecs) {
274 writeln!(report, "Entity: {}", entity.id()).unwrap();
275 let archetype = entity.archetype();
276 let component_count = archetype.component_count();
277
278 let component_names = archetype
279 .components()
280 .iter()
281 .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
282 .collect::<Vec<_>>();
283 writeln!(
284 report,
285 "- {component_count} components: {}",
286 component_names.join(", ")
287 )
288 .unwrap();
289 }
290
291 writeln!(report).unwrap();
292
293
294 for (info, _) in ecs.iter_resources() {
295 let name = info.name().to_string();
296 writeln!(report, "Resource: {name}").unwrap();
297 // writeln!(report, "- Size: {} bytes",
298 // info.layout().size()).unwrap();
299
300 match name.as_ref() {
301 "azalea_world::container::InstanceContainer" => {
302 let instance_container = ecs.resource::<InstanceContainer>();
303
304 for (instance_name, instance) in &instance_container.instances {
305 writeln!(report, "- Name: {instance_name}").unwrap();
306 writeln!(report, "- Reference count: {}", instance.strong_count())
307 .unwrap();
308 if let Some(instance) = instance.upgrade() {
309 let instance = instance.read();
310 let strong_chunks = instance
311 .chunks
312 .map
313 .iter()
314 .filter(|(_, v)| v.strong_count() > 0)
315 .count();
316 writeln!(
317 report,
318 "- Chunks: {} strongly referenced, {} in map",
319 strong_chunks,
320 instance.chunks.map.len()
321 )
322 .unwrap();
323 writeln!(
324 report,
325 "- Entities: {}",
326 instance.entities_by_chunk.len()
327 )
328 .unwrap();
329 }
330 }
331 }
332 "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
333 let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
334 writeln!(report, "- Event count: {}", events.len()).unwrap();
335 }
336 "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
337 let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
338 writeln!(report, "- Event count: {}", events.len()).unwrap();
339 }
340
341 _ => {}
342 }
343 }
344
345 println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
346 });
347
348 1
349 }));
350
351 commands.register(literal("exit").executes(|ctx: &Ctx| {
352 let source = ctx.source.lock();
353 source.reply("bye!");
354
355 source.bot.disconnect();
356
357 let source = ctx.source.clone();
358 thread::spawn(move || {
359 thread::sleep(Duration::from_secs(1));
360
361 source.lock().bot.ecs.lock().write_message(AppExit::Success);
362 });
363
364 1
365 }));
366}Source§impl Client
impl Client
Return the menu that is currently open, or the player’s inventory if no menu is open.
Sourcepub fn selected_hotbar_slot(&self) -> u8
pub fn selected_hotbar_slot(&self) -> u8
Returns the index of the hotbar slot that’s currently selected.
If you want to access the actual held item, you can get the current menu
with Client::menu and then get the slot index by offsetting from
the start of azalea_inventory::Menu::hotbar_slots_range.
You can use Self::set_selected_hotbar_slot to change it.
Sourcepub fn set_selected_hotbar_slot(&self, new_hotbar_slot_index: u8)
pub fn set_selected_hotbar_slot(&self, new_hotbar_slot_index: u8)
Update the selected hotbar slot index.
This will run next Update, so you might want to call
bot.wait_updates(1) after calling this if you’re using azalea.
§Panics
This will panic if new_hotbar_slot_index is not in the range 0..=8.
Source§impl Client
impl Client
pub fn start_mining(&self, position: BlockPos)
Sourcepub fn left_click_mine(&self, enabled: bool)
pub fn left_click_mine(&self, enabled: bool)
When enabled, the bot will mine any block that it is looking at if it is reachable.
Source§impl Client
impl Client
Sourcepub fn set_jumping(&self, jumping: bool)
pub fn set_jumping(&self, jumping: bool)
Set whether we’re jumping. This acts as if you held space in vanilla.
If you want to jump once, use the jump function in azalea.
If you’re making a realistic client, calling this function every tick is recommended.
Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Sourcepub fn set_crouching(&self, crouching: bool)
pub fn set_crouching(&self, crouching: bool)
Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Sourcepub fn crouching(&self) -> bool
pub fn crouching(&self) -> bool
Whether the client is currently trying to sneak.
You may want to check the [Pose] instead.
Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Sourcepub fn set_direction(&self, y_rot: f32, x_rot: f32)
pub fn set_direction(&self, y_rot: f32, x_rot: f32)
Sets the direction the client is looking.
y_rot is yaw (looking to the side, between -180 to 180), and x_rot
is pitch (looking up and down, between -90 to 90).
You can get these numbers from the vanilla f3 screen.
Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Sourcepub fn direction(&self) -> (f32, f32)
pub fn direction(&self) -> (f32, f32)
Returns the direction the client is looking.
See Self::set_direction for more details.
Source§impl Client
impl Client
Sourcepub fn walk(&self, direction: WalkDirection)
pub fn walk(&self, direction: WalkDirection)
Start walking in the given direction.
To sprint, use Client::sprint. To stop walking, call walk with
WalkDirection::None.
§Example
// walk for one second
bot.walk(WalkDirection::Forward);
tokio::time::sleep(Duration::from_secs(1)).await;
bot.walk(WalkDirection::None);Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Sourcepub fn sprint(&self, direction: SprintDirection)
pub fn sprint(&self, direction: SprintDirection)
Start sprinting in the given direction.
o stop moving, call bot.walk(WalkDirection::None)
§Example
// sprint for one second
bot.sprint(SprintDirection::Forward);
tokio::time::sleep(Duration::from_secs(1)).await;
bot.walk(WalkDirection::None);Examples found in repository?
16pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
17 commands.register(
18 literal("goto")
19 .executes(|ctx: &Ctx| {
20 let mut source = ctx.source.lock();
21 println!("got goto");
22 // look for the sender
23 let Some(entity) = source.entity() else {
24 source.reply("I can't see you!");
25 return 0;
26 };
27 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
28 source.reply("I can't see you!");
29 return 0;
30 };
31 source.reply("ok");
32 source
33 .bot
34 .start_goto(BlockPosGoal(BlockPos::from(position)));
35 1
36 })
37 .then(literal("xz").then(argument("x", integer()).then(
38 argument("z", integer()).executes(|ctx: &Ctx| {
39 let source = ctx.source.lock();
40 let x = get_integer(ctx, "x").unwrap();
41 let z = get_integer(ctx, "z").unwrap();
42 println!("goto xz {x} {z}");
43 source.reply("ok");
44 source.bot.start_goto(XZGoal { x, z });
45 1
46 }),
47 )))
48 .then(literal("radius").then(argument("radius", float()).then(
49 argument("x", integer()).then(argument("y", integer()).then(
50 argument("z", integer()).executes(|ctx: &Ctx| {
51 let source = ctx.source.lock();
52 let radius = get_float(ctx, "radius").unwrap();
53 let x = get_integer(ctx, "x").unwrap();
54 let y = get_integer(ctx, "y").unwrap();
55 let z = get_integer(ctx, "z").unwrap();
56 println!("goto radius {radius}, position: {x} {y} {z}");
57 source.reply("ok");
58 source.bot.start_goto(RadiusGoal {
59 pos: BlockPos::new(x, y, z).center(),
60 radius,
61 });
62 1
63 }),
64 )),
65 )))
66 .then(argument("x", integer()).then(argument("y", integer()).then(
67 argument("z", integer()).executes(|ctx: &Ctx| {
68 let source = ctx.source.lock();
69 let x = get_integer(ctx, "x").unwrap();
70 let y = get_integer(ctx, "y").unwrap();
71 let z = get_integer(ctx, "z").unwrap();
72 println!("goto xyz {x} {y} {z}");
73 source.reply("ok");
74 source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
75 1
76 }),
77 ))),
78 );
79
80 commands.register(literal("down").executes(|ctx: &Ctx| {
81 let source = ctx.source.clone();
82 tokio::spawn(async move {
83 let bot = source.lock().bot.clone();
84 let position = BlockPos::from(bot.position());
85 source.lock().reply("mining...");
86 bot.mine(position.down(1)).await;
87 source.lock().reply("done");
88 });
89 1
90 }));
91
92 commands.register(
93 literal("look")
94 .executes(|ctx: &Ctx| {
95 // look for the sender
96 let mut source = ctx.source.lock();
97 let Some(entity) = source.entity() else {
98 source.reply("I can't see you!");
99 return 0;
100 };
101 let Some(position) = source.bot.get_entity_component::<Position>(entity) else {
102 source.reply("I can't see you!");
103 return 0;
104 };
105 let eye_height = source
106 .bot
107 .get_entity_component::<EntityDimensions>(entity)
108 .map(|h| h.eye_height)
109 .unwrap_or_default();
110 source.bot.look_at(position.up(eye_height as f64));
111 1
112 })
113 .then(argument("x", integer()).then(argument("y", integer()).then(
114 argument("z", integer()).executes(|ctx: &Ctx| {
115 let pos = BlockPos::new(
116 get_integer(ctx, "x").unwrap(),
117 get_integer(ctx, "y").unwrap(),
118 get_integer(ctx, "z").unwrap(),
119 );
120 println!("{pos:?}");
121 let source = ctx.source.lock();
122 source.bot.look_at(pos.center());
123 1
124 }),
125 ))),
126 );
127
128 commands.register(
129 literal("walk").then(argument("seconds", float()).executes(|ctx: &Ctx| {
130 let mut seconds = get_float(ctx, "seconds").unwrap();
131 let source = ctx.source.lock();
132 let bot = source.bot.clone();
133
134 if seconds < 0. {
135 bot.walk(WalkDirection::Backward);
136 seconds = -seconds;
137 } else {
138 bot.walk(WalkDirection::Forward);
139 }
140
141 tokio::spawn(async move {
142 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
143 bot.walk(WalkDirection::None);
144 });
145 source.reply(format!("ok, walking for {seconds} seconds"));
146 1
147 })),
148 );
149 commands.register(
150 literal("sprint").then(argument("seconds", float()).executes(|ctx: &Ctx| {
151 let seconds = get_float(ctx, "seconds").unwrap();
152 let source = ctx.source.lock();
153 let bot = source.bot.clone();
154 bot.sprint(SprintDirection::Forward);
155 tokio::spawn(async move {
156 tokio::time::sleep(Duration::from_secs_f32(seconds)).await;
157 bot.walk(WalkDirection::None);
158 });
159 source.reply(format!("ok, sprinting for {seconds} seconds"));
160 1
161 })),
162 );
163
164 commands.register(literal("north").executes(|ctx: &Ctx| {
165 let source = ctx.source.lock();
166 source.bot.set_direction(180., 0.);
167 source.reply("ok");
168 1
169 }));
170 commands.register(literal("south").executes(|ctx: &Ctx| {
171 let source = ctx.source.lock();
172 source.bot.set_direction(0., 0.);
173 source.reply("ok");
174 1
175 }));
176 commands.register(literal("east").executes(|ctx: &Ctx| {
177 let source = ctx.source.lock();
178 source.bot.set_direction(-90., 0.);
179 source.reply("ok");
180 1
181 }));
182 commands.register(literal("west").executes(|ctx: &Ctx| {
183 let source = ctx.source.lock();
184 source.bot.set_direction(90., 0.);
185 source.reply("ok");
186 1
187 }));
188 commands.register(
189 literal("jump")
190 .executes(|ctx: &Ctx| {
191 let source = ctx.source.lock();
192 source.bot.jump();
193 source.reply("ok");
194 1
195 })
196 .then(argument("enabled", bool()).executes(|ctx: &Ctx| {
197 let jumping = get_bool(ctx, "enabled").unwrap();
198 let source = ctx.source.lock();
199 source.bot.set_jumping(jumping);
200 1
201 })),
202 );
203
204 let sneak = |ctx: &Ctx| {
205 let source = ctx.source.lock();
206 source.bot.set_crouching(!source.bot.crouching());
207 source.reply("ok");
208 1
209 };
210 let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
211 let sneaking = get_bool(ctx, "enabled").unwrap();
212 let source = ctx.source.lock();
213 source.bot.set_crouching(sneaking);
214 1
215 });
216 commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
217 commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
218
219 commands.register(literal("stop").executes(|ctx: &Ctx| {
220 let source = ctx.source.lock();
221 source.bot.stop_pathfinding();
222 source.reply("ok");
223 *source.state.task.lock() = BotTask::None;
224 1
225 }));
226}Trait Implementations§
Source§impl AutoToolClientExt for Client
impl AutoToolClientExt for Client
fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult
async fn mine_with_auto_tool(&self, block_pos: BlockPos)
Source§impl BotClientExt for Client
impl BotClientExt for Client
Source§fn get_tick_broadcaster(&self) -> Receiver<()>
fn get_tick_broadcaster(&self) -> Receiver<()>
Returns a Receiver that receives a message every game tick.
This is useful if you want to efficiently loop until a certain condition is met.
let mut ticks = bot.get_tick_broadcaster();
while ticks.recv().await.is_ok() {
let ecs = bot.ecs.lock();
if ecs.get::<WaitingForInventoryOpen>(bot.entity).is_none() {
break;
}
}Source§fn get_update_broadcaster(&self) -> Receiver<()>
fn get_update_broadcaster(&self) -> Receiver<()>
Returns a Receiver that receives a message every ECS Update.
ECS Updates happen at least at the frequency of game ticks, usually faster.
This is useful if you’re sending an ECS event and want to make sure it’s been handled before continuing.
Source§async fn wait_ticks(&self, n: usize)
async fn wait_ticks(&self, n: usize)
Wait for the specified number of ticks using
Self::get_tick_broadcaster.
If you’re going to run this in a loop, you may want to use that function
instead and use the Receiver from it to avoid accidentally skipping
ticks and having to wait longer.
Source§async fn wait_updates(&self, n: usize)
async fn wait_updates(&self, n: usize)
Waits for the specified number of ECS Updates using
Self::get_update_broadcaster.
These are basically equivalent to frames because even though we have no rendering, some game mechanics depend on frames.
If you’re going to run this in a loop, you may want to use that function
instead and use the Receiver from it to avoid accidentally skipping
ticks and having to wait longer.
Source§impl ContainerClientExt for Client
impl ContainerClientExt for Client
Source§async fn open_container_at(&self, pos: BlockPos) -> Option<ContainerHandle>
async fn open_container_at(&self, pos: BlockPos) -> Option<ContainerHandle>
Source§async fn open_container_at_with_timeout_ticks(
&self,
pos: BlockPos,
timeout_ticks: Option<usize>,
) -> Option<ContainerHandle>
async fn open_container_at_with_timeout_ticks( &self, pos: BlockPos, timeout_ticks: Option<usize>, ) -> Option<ContainerHandle>
Source§async fn wait_for_container_open(
&self,
timeout_ticks: Option<usize>,
) -> Option<ContainerHandle>
async fn wait_for_container_open( &self, timeout_ticks: Option<usize>, ) -> Option<ContainerHandle>
Source§fn open_inventory(&self) -> Option<ContainerHandle>
fn open_inventory(&self) -> Option<ContainerHandle>
Source§fn get_inventory(&self) -> ContainerHandleRef
fn get_inventory(&self) -> ContainerHandleRef
ContainerHandleRef to the client’s currently open
container, or their inventory. Read moreSource§fn get_held_item(&self) -> ItemStack
fn get_held_item(&self) -> ItemStack
Source§impl PathfinderClientExt for Client
impl PathfinderClientExt for Client
Source§async fn goto(&self, goal: impl Goal + 'static)
async fn goto(&self, goal: impl Goal + 'static)
Source§async fn goto_with_opts(&self, goal: impl Goal + 'static, opts: PathfinderOpts)
async fn goto_with_opts(&self, goal: impl Goal + 'static, opts: PathfinderOpts)
Self::goto, but allows you to set custom options for
pathfinding, including disabling mining and setting custom moves. Read moreSource§fn start_goto(&self, goal: impl Goal + 'static)
fn start_goto(&self, goal: impl Goal + 'static)
Source§fn start_goto_with_opts(&self, goal: impl Goal + 'static, opts: PathfinderOpts)
fn start_goto_with_opts(&self, goal: impl Goal + 'static, opts: PathfinderOpts)
Self::start_goto, but allows you to set custom
options for pathfinding, including disabling mining and setting custom
moves. Read moreSource§fn stop_pathfinding(&self)
fn stop_pathfinding(&self)
Source§fn force_stop_pathfinding(&self)
fn force_stop_pathfinding(&self)
Source§async fn wait_until_goto_target_reached(&self)
async fn wait_until_goto_target_reached(&self)
Source§fn is_goto_target_reached(&self) -> bool
fn is_goto_target_reached(&self) -> bool
Auto Trait Implementations§
impl Freeze for Client
impl !RefUnwindSafe for Client
impl Send for Client
impl Sync for Client
impl Unpin for Client
impl !UnwindSafe for Client
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.