Skip to main content

World

Struct World 

Source
pub struct World {
    pub chunks: ChunkStorage,
    pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
    pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
    pub registries: RegistryHolder,
}
Expand description

A Minecraft world, sometimes referred to as a dimension.

This is the most commonly used type to interact with the world that a client is in. In here, all chunks are stored as weak pointers, which means that clients in different areas of the same world can share the same World instance.

Also see PartialWorld.

Note that this is distinct from Bevy’s World type, which contains all of the data for every client. When referring to the Bevy World within Azalea, the term “ECS” is generally used instead.

Fields§

§chunks: ChunkStorage§entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>

An index of all the entities we know are in the chunks of the world

§entity_by_id: IntMap<MinecraftEntityId, Entity>

An index of Minecraft entity IDs to Azalea ECS entities.

You should avoid using this (particularly if you’re using swarms) and instead use azalea_entity::EntityIdIndex, since some servers may give different entity IDs for the same entities to different players.

§registries: RegistryHolder

Implementations§

Source§

impl World

Source

pub fn find_block( &self, nearest_to: impl Into<BlockPos>, block_states: &BlockStates, ) -> Option<BlockPos>

Find the coordinates of a block in the world.

Note that this is sorted by x+y+z and not x^2+y^2+z^2 for performance purposes.

let pos = world.find_block(position, &BlockKind::Chest.into());
Source

pub fn find_blocks<'a>( &'a self, nearest_to: impl Into<BlockPos>, block_states: &'a BlockStates, ) -> FindBlocks<'a>

Find all the coordinates of a block in the world.

This returns an iterator that yields the BlockPoss of blocks that are in the given block states.

Note that this is sorted by x+y+z and not x^2+y^2+z^2 for performance purposes.

Examples found in repository?
azalea/examples/steal.rs (line 58)
42async fn steal(bot: Client, state: State) -> eyre::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.left_click(index);
85            }
86        }
87    }
88
89    bot.chat("Done");
90
91    *state.is_stealing.lock() = false;
92
93    Ok(())
94}
Source§

impl World

Source

pub fn get_block_state(&self, pos: BlockPos) -> Option<BlockState>

Get the block at the given position, or None if it’s outside of the world that we have loaded.

Examples found in repository?
azalea/examples/testbot/commands/debug.rs (line 129)
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    commands.register(
33        literal("say").then(argument("message", greedy_string()).executes(|ctx: &Ctx| {
34            let source = ctx.source.lock();
35            let message = get_string(ctx, "message").unwrap();
36            source.bot.chat(message);
37            1
38        })),
39    );
40
41    commands.register(literal("disconnect").executes(|ctx: &Ctx| {
42        let source = ctx.source.lock();
43        source.bot.disconnect();
44        1
45    }));
46
47    commands.register(literal("whereami").executes(|ctx: &Ctx| {
48        let source = ctx.source.lock();
49        let Some(entity) = source.entity() else {
50            source.reply("You aren't in render distance!");
51            return 0;
52        };
53        let position = entity.position();
54        source.reply(format!(
55            "You are at {}, {}, {}",
56            position.x, position.y, position.z
57        ));
58        1
59    }));
60
61    commands.register(literal("entityid").executes(|ctx: &Ctx| {
62        let source = ctx.source.lock();
63        let Some(entity) = source.entity() else {
64            source.reply("You aren't in render distance!");
65            return 0;
66        };
67        let entity_id = entity.minecraft_id();
68        source.reply(format!(
69            "Your Minecraft ID is {} and your ECS ID is {entity:?}",
70            *entity_id
71        ));
72        1
73    }));
74
75    let whereareyou = |ctx: &Ctx| {
76        let source = ctx.source.lock();
77        let position = source.bot.position();
78        source.reply(format!(
79            "I'm at {}, {}, {}",
80            position.x, position.y, position.z
81        ));
82        1
83    };
84    commands.register(literal("whereareyou").executes(whereareyou));
85    commands.register(literal("pos").executes(whereareyou));
86
87    commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
88        let source = ctx.source.lock();
89        source.reply(format!(
90            "I am {} ({}, {})",
91            source.bot.username(),
92            source.bot.uuid(),
93            source.bot.entity
94        ));
95        1
96    }));
97
98    commands.register(literal("getdirection").executes(|ctx: &Ctx| {
99        let source = ctx.source.lock();
100        let direction = source.bot.direction();
101        source.reply(format!(
102            "I'm looking at {}, {}",
103            direction.y_rot(),
104            direction.x_rot()
105        ));
106        1
107    }));
108
109    commands.register(literal("health").executes(|ctx: &Ctx| {
110        let source = ctx.source.lock();
111
112        let health = source.bot.health();
113        source.reply(format!("I have {health} health"));
114        1
115    }));
116
117    commands.register(literal("lookingat").executes(|ctx: &Ctx| {
118        let source = ctx.source.lock();
119
120        let hit_result = source.bot.hit_result();
121
122        match &hit_result {
123            HitResult::Block(r) => {
124                if r.miss {
125                    source.reply("I'm not looking at anything");
126                    return 0;
127                }
128                let block_pos = r.block_pos;
129                let block = source.bot.world().read().get_block_state(block_pos);
130                source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
131            }
132            HitResult::Entity(r) => {
133                let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
134                source.reply(format!(
135                    "I'm looking at {entity_kind} ({:?}) at {}",
136                    r.entity, r.location
137                ));
138            }
139        }
140
141        1
142    }));
143
144    commands.register(literal("getblock").then(argument("x", integer()).then(
145        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
146            let source = ctx.source.lock();
147            let x = get_integer(ctx, "x").unwrap();
148            let y = get_integer(ctx, "y").unwrap();
149            let z = get_integer(ctx, "z").unwrap();
150            println!("getblock xyz {x} {y} {z}");
151            let block_pos = BlockPos::new(x, y, z);
152            let block = source.bot.world().read().get_block_state(block_pos);
153            source.reply(format!("BlockKind at {block_pos} is {block:?}"));
154            1
155        })),
156    )));
157    commands.register(literal("getfluid").then(argument("x", integer()).then(
158        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
159            let source = ctx.source.lock();
160            let x = get_integer(ctx, "x").unwrap();
161            let y = get_integer(ctx, "y").unwrap();
162            let z = get_integer(ctx, "z").unwrap();
163            println!("getfluid xyz {x} {y} {z}");
164            let block_pos = BlockPos::new(x, y, z);
165            let block = source.bot.world().read().get_fluid_state(block_pos);
166            source.reply(format!("Fluid at {block_pos} is {block:?}"));
167            1
168        })),
169    )));
170
171    commands.register(literal("inventory").executes(|ctx: &Ctx| {
172        let source = ctx.source.lock();
173        for item in source.bot.menu().slots() {
174            if item.is_empty() {
175                continue;
176            }
177            println!("{item:?}");
178            for (kind, data) in item.component_patch().iter() {
179                if let Some(data) = data {
180                    println!("- {kind} {data:?}");
181                }
182            }
183        }
184        1
185    }));
186
187    commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
188        let source = ctx.source.lock();
189        let pathfinder = source.bot.get_component::<Pathfinder>();
190        let Some(pathfinder) = pathfinder else {
191            source.reply("I don't have the Pathfinder component");
192            return 1;
193        };
194        source.reply(format!(
195            "pathfinder.is_calculating: {}",
196            pathfinder.is_calculating
197        ));
198
199        let executing_path = source.bot.get_component::<ExecutingPath>();
200        let Some(executing_path) = executing_path else {
201            source.reply("I'm not executing a path");
202            return 1;
203        };
204        source.reply(format!(
205            "is_path_partial: {}, path.len: {}, queued_path.len: {}",
206            executing_path.is_path_partial,
207            executing_path.path.len(),
208            if let Some(queued) = &executing_path.queued_path {
209                queued.len().to_string()
210            } else {
211                "n/a".to_owned()
212            },
213        ));
214        1
215    }));
216    commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
217        let source = ctx.source.lock();
218
219        let Some(entity) = source.entity() else {
220            source.reply("You aren't in render distance!");
221            return 0;
222        };
223        let position = entity.position();
224        let position = BlockPos::from(position);
225
226        let mut edges = Vec::new();
227        let cached_world = CachedWorld::new(source.bot.world(), position);
228        let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
229        let custom_state = CustomPathfinderStateRef::default();
230
231        azalea::pathfinder::moves::default_move(
232            &mut MovesCtx {
233                edges: &mut edges,
234                world: &cached_world,
235                mining_cache: &mining_cache,
236                custom_state: &custom_state,
237            },
238            RelBlockPos::from_origin(position, position),
239        );
240
241        if edges.is_empty() {
242            source.reply("No possible moves.");
243        } else {
244            source.reply("Moves:");
245            for (i, edge) in edges.iter().enumerate() {
246                source.reply(format!("{}) {edge:?}", i + 1));
247            }
248        }
249
250        1
251    }));
252
253    commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
254        let source = ctx.source.lock();
255        source.bot.start_use_item();
256        source.reply("Ok!");
257        1
258    }));
259    commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
260        let source = ctx.source.lock();
261        let max_stack_size = source
262            .bot
263            .get_held_item()
264            .get_component::<MaxStackSize>()
265            .map_or(-1, |s| s.count);
266        source.reply(format!("{max_stack_size}"));
267        1
268    }));
269
270    commands.register(literal("dimensions").executes(|ctx: &Ctx| {
271        let source = ctx.source.lock();
272        let bot_dimensions = source.bot.dimensions();
273        source.reply(format!("{bot_dimensions:?}"));
274        1
275    }));
276
277    commands.register(literal("players").executes(|ctx: &Ctx| {
278        let source = ctx.source.lock();
279        let player_entities = source
280            .bot
281            .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
282        let tab_list = source.bot.tab_list();
283        for player_entity in player_entities {
284            let uuid = player_entity.uuid();
285            source.reply(format!(
286                "{} - {} ({:?})",
287                player_entity.id(),
288                tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
289                uuid
290            ));
291        }
292        1
293    }));
294
295    commands.register(literal("enchants").executes(|ctx: &Ctx| {
296        let source = ctx.source.lock();
297        source.bot.with_registry_holder(|r| {
298            let enchants = &r.enchantment;
299            println!("enchants: {enchants:?}");
300        });
301        1
302    }));
303
304    commands.register(literal("attributes").executes(|ctx: &Ctx| {
305        let source = ctx.source.lock();
306        let attributes = source.bot.attributes();
307        println!("attributes: {attributes:?}");
308        1
309    }));
310
311    commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
312        let source = ctx.source.lock();
313
314        source.reply("Ok!");
315
316
317
318        source.bot.disconnect();
319
320        let ecs = source.bot.ecs.clone();
321        thread::spawn(move || {
322            thread::sleep(Duration::from_secs(1));
323            // dump the ecs
324
325            let mut ecs = ecs.write();
326
327            let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
328            let mut report = File::create(&report_path).unwrap();
329
330            let mut query = ecs.query::<EntityRef>();
331            for entity in query.iter(& ecs) {
332                writeln!(report, "Entity: {}", entity.id()).unwrap();
333                let archetype = entity.archetype();
334                let component_count = archetype.component_count();
335
336                let component_names = archetype
337                    .components()
338                    .iter()
339                    .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
340                    .collect::<Vec<_>>();
341                writeln!(
342                    report,
343                    "- {component_count} components: {}",
344                    component_names.join(", ")
345                )
346                .unwrap();
347            }
348
349            writeln!(report).unwrap();
350
351
352            for (info, _) in ecs.iter_resources() {
353                let name = info.name().to_string();
354                writeln!(report, "Resource: {name}").unwrap();
355                // writeln!(report, "- Size: {} bytes",
356                // info.layout().size()).unwrap();
357
358                match name.as_ref() {
359                    "azalea_world::container::Worlds" => {
360                        let worlds = ecs.resource::<Worlds>();
361
362                        for (world_name, world) in &worlds.map {
363                            writeln!(report, "- Name: {world_name}").unwrap();
364                            writeln!(report, "- Reference count: {}", world.strong_count())
365                                .unwrap();
366                            if let Some(world) = world.upgrade() {
367                                let world = world.read();
368                                let chunks = &world.chunks;
369                                let chunks = (chunks as &dyn Any).downcast_ref::<WeakChunkStorage>();
370                                if let Some(chunks) = chunks {
371                                    let strong_chunks = chunks
372                                        .map
373                                        .iter()
374                                        .filter(|(_, v)| v.strong_count() > 0)
375                                        .count();
376                                    writeln!(
377                                        report,
378                                        "- Chunks: {} strongly referenced, {} in map",
379                                        strong_chunks,
380                                        chunks.map.len()
381                                    )
382                                    .unwrap();
383                                }
384                                writeln!(
385                                    report,
386                                    "- Entities: {}",
387                                    world.entities_by_chunk.len()
388                                )
389                                .unwrap();
390                            }
391                        }
392                    }
393                    "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
394                        let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
395                        writeln!(report, "- Event count: {}", events.len()).unwrap();
396                    }
397                    "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
398                        let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
399                        writeln!(report, "- Event count: {}", events.len()).unwrap();
400                    }
401
402                    _ => {}
403                }
404            }
405
406            println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
407        });
408
409        1
410    }));
411
412    commands.register(literal("exit").executes(|ctx: &Ctx| {
413        let source = ctx.source.lock();
414        source.reply("bye!");
415
416        source.bot.disconnect();
417
418        let source = ctx.source.clone();
419        thread::spawn(move || {
420            thread::sleep(Duration::from_secs(1));
421
422            source
423                .lock()
424                .bot
425                .ecs
426                .write()
427                .write_message(AppExit::Success);
428        });
429
430        1
431    }));
432}
Source

pub fn get_fluid_state(&self, pos: BlockPos) -> Option<FluidState>

Similar to Self::get_block_state, but returns data about the fluid at the position, including for waterlogged blocks.

Examples found in repository?
azalea/examples/testbot/commands/debug.rs (line 165)
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    commands.register(
33        literal("say").then(argument("message", greedy_string()).executes(|ctx: &Ctx| {
34            let source = ctx.source.lock();
35            let message = get_string(ctx, "message").unwrap();
36            source.bot.chat(message);
37            1
38        })),
39    );
40
41    commands.register(literal("disconnect").executes(|ctx: &Ctx| {
42        let source = ctx.source.lock();
43        source.bot.disconnect();
44        1
45    }));
46
47    commands.register(literal("whereami").executes(|ctx: &Ctx| {
48        let source = ctx.source.lock();
49        let Some(entity) = source.entity() else {
50            source.reply("You aren't in render distance!");
51            return 0;
52        };
53        let position = entity.position();
54        source.reply(format!(
55            "You are at {}, {}, {}",
56            position.x, position.y, position.z
57        ));
58        1
59    }));
60
61    commands.register(literal("entityid").executes(|ctx: &Ctx| {
62        let source = ctx.source.lock();
63        let Some(entity) = source.entity() else {
64            source.reply("You aren't in render distance!");
65            return 0;
66        };
67        let entity_id = entity.minecraft_id();
68        source.reply(format!(
69            "Your Minecraft ID is {} and your ECS ID is {entity:?}",
70            *entity_id
71        ));
72        1
73    }));
74
75    let whereareyou = |ctx: &Ctx| {
76        let source = ctx.source.lock();
77        let position = source.bot.position();
78        source.reply(format!(
79            "I'm at {}, {}, {}",
80            position.x, position.y, position.z
81        ));
82        1
83    };
84    commands.register(literal("whereareyou").executes(whereareyou));
85    commands.register(literal("pos").executes(whereareyou));
86
87    commands.register(literal("whoareyou").executes(|ctx: &Ctx| {
88        let source = ctx.source.lock();
89        source.reply(format!(
90            "I am {} ({}, {})",
91            source.bot.username(),
92            source.bot.uuid(),
93            source.bot.entity
94        ));
95        1
96    }));
97
98    commands.register(literal("getdirection").executes(|ctx: &Ctx| {
99        let source = ctx.source.lock();
100        let direction = source.bot.direction();
101        source.reply(format!(
102            "I'm looking at {}, {}",
103            direction.y_rot(),
104            direction.x_rot()
105        ));
106        1
107    }));
108
109    commands.register(literal("health").executes(|ctx: &Ctx| {
110        let source = ctx.source.lock();
111
112        let health = source.bot.health();
113        source.reply(format!("I have {health} health"));
114        1
115    }));
116
117    commands.register(literal("lookingat").executes(|ctx: &Ctx| {
118        let source = ctx.source.lock();
119
120        let hit_result = source.bot.hit_result();
121
122        match &hit_result {
123            HitResult::Block(r) => {
124                if r.miss {
125                    source.reply("I'm not looking at anything");
126                    return 0;
127                }
128                let block_pos = r.block_pos;
129                let block = source.bot.world().read().get_block_state(block_pos);
130                source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
131            }
132            HitResult::Entity(r) => {
133                let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
134                source.reply(format!(
135                    "I'm looking at {entity_kind} ({:?}) at {}",
136                    r.entity, r.location
137                ));
138            }
139        }
140
141        1
142    }));
143
144    commands.register(literal("getblock").then(argument("x", integer()).then(
145        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
146            let source = ctx.source.lock();
147            let x = get_integer(ctx, "x").unwrap();
148            let y = get_integer(ctx, "y").unwrap();
149            let z = get_integer(ctx, "z").unwrap();
150            println!("getblock xyz {x} {y} {z}");
151            let block_pos = BlockPos::new(x, y, z);
152            let block = source.bot.world().read().get_block_state(block_pos);
153            source.reply(format!("BlockKind at {block_pos} is {block:?}"));
154            1
155        })),
156    )));
157    commands.register(literal("getfluid").then(argument("x", integer()).then(
158        argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
159            let source = ctx.source.lock();
160            let x = get_integer(ctx, "x").unwrap();
161            let y = get_integer(ctx, "y").unwrap();
162            let z = get_integer(ctx, "z").unwrap();
163            println!("getfluid xyz {x} {y} {z}");
164            let block_pos = BlockPos::new(x, y, z);
165            let block = source.bot.world().read().get_fluid_state(block_pos);
166            source.reply(format!("Fluid at {block_pos} is {block:?}"));
167            1
168        })),
169    )));
170
171    commands.register(literal("inventory").executes(|ctx: &Ctx| {
172        let source = ctx.source.lock();
173        for item in source.bot.menu().slots() {
174            if item.is_empty() {
175                continue;
176            }
177            println!("{item:?}");
178            for (kind, data) in item.component_patch().iter() {
179                if let Some(data) = data {
180                    println!("- {kind} {data:?}");
181                }
182            }
183        }
184        1
185    }));
186
187    commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
188        let source = ctx.source.lock();
189        let pathfinder = source.bot.get_component::<Pathfinder>();
190        let Some(pathfinder) = pathfinder else {
191            source.reply("I don't have the Pathfinder component");
192            return 1;
193        };
194        source.reply(format!(
195            "pathfinder.is_calculating: {}",
196            pathfinder.is_calculating
197        ));
198
199        let executing_path = source.bot.get_component::<ExecutingPath>();
200        let Some(executing_path) = executing_path else {
201            source.reply("I'm not executing a path");
202            return 1;
203        };
204        source.reply(format!(
205            "is_path_partial: {}, path.len: {}, queued_path.len: {}",
206            executing_path.is_path_partial,
207            executing_path.path.len(),
208            if let Some(queued) = &executing_path.queued_path {
209                queued.len().to_string()
210            } else {
211                "n/a".to_owned()
212            },
213        ));
214        1
215    }));
216    commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
217        let source = ctx.source.lock();
218
219        let Some(entity) = source.entity() else {
220            source.reply("You aren't in render distance!");
221            return 0;
222        };
223        let position = entity.position();
224        let position = BlockPos::from(position);
225
226        let mut edges = Vec::new();
227        let cached_world = CachedWorld::new(source.bot.world(), position);
228        let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
229        let custom_state = CustomPathfinderStateRef::default();
230
231        azalea::pathfinder::moves::default_move(
232            &mut MovesCtx {
233                edges: &mut edges,
234                world: &cached_world,
235                mining_cache: &mining_cache,
236                custom_state: &custom_state,
237            },
238            RelBlockPos::from_origin(position, position),
239        );
240
241        if edges.is_empty() {
242            source.reply("No possible moves.");
243        } else {
244            source.reply("Moves:");
245            for (i, edge) in edges.iter().enumerate() {
246                source.reply(format!("{}) {edge:?}", i + 1));
247            }
248        }
249
250        1
251    }));
252
253    commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
254        let source = ctx.source.lock();
255        source.bot.start_use_item();
256        source.reply("Ok!");
257        1
258    }));
259    commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
260        let source = ctx.source.lock();
261        let max_stack_size = source
262            .bot
263            .get_held_item()
264            .get_component::<MaxStackSize>()
265            .map_or(-1, |s| s.count);
266        source.reply(format!("{max_stack_size}"));
267        1
268    }));
269
270    commands.register(literal("dimensions").executes(|ctx: &Ctx| {
271        let source = ctx.source.lock();
272        let bot_dimensions = source.bot.dimensions();
273        source.reply(format!("{bot_dimensions:?}"));
274        1
275    }));
276
277    commands.register(literal("players").executes(|ctx: &Ctx| {
278        let source = ctx.source.lock();
279        let player_entities = source
280            .bot
281            .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
282        let tab_list = source.bot.tab_list();
283        for player_entity in player_entities {
284            let uuid = player_entity.uuid();
285            source.reply(format!(
286                "{} - {} ({:?})",
287                player_entity.id(),
288                tab_list.get(&uuid).map_or("?", |p| p.profile.name.as_str()),
289                uuid
290            ));
291        }
292        1
293    }));
294
295    commands.register(literal("enchants").executes(|ctx: &Ctx| {
296        let source = ctx.source.lock();
297        source.bot.with_registry_holder(|r| {
298            let enchants = &r.enchantment;
299            println!("enchants: {enchants:?}");
300        });
301        1
302    }));
303
304    commands.register(literal("attributes").executes(|ctx: &Ctx| {
305        let source = ctx.source.lock();
306        let attributes = source.bot.attributes();
307        println!("attributes: {attributes:?}");
308        1
309    }));
310
311    commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
312        let source = ctx.source.lock();
313
314        source.reply("Ok!");
315
316
317
318        source.bot.disconnect();
319
320        let ecs = source.bot.ecs.clone();
321        thread::spawn(move || {
322            thread::sleep(Duration::from_secs(1));
323            // dump the ecs
324
325            let mut ecs = ecs.write();
326
327            let report_path = env::temp_dir().join("azalea-ecs-leak-report.txt");
328            let mut report = File::create(&report_path).unwrap();
329
330            let mut query = ecs.query::<EntityRef>();
331            for entity in query.iter(& ecs) {
332                writeln!(report, "Entity: {}", entity.id()).unwrap();
333                let archetype = entity.archetype();
334                let component_count = archetype.component_count();
335
336                let component_names = archetype
337                    .components()
338                    .iter()
339                    .map(|c| ecs.components().get_info(*c).unwrap().name().to_string())
340                    .collect::<Vec<_>>();
341                writeln!(
342                    report,
343                    "- {component_count} components: {}",
344                    component_names.join(", ")
345                )
346                .unwrap();
347            }
348
349            writeln!(report).unwrap();
350
351
352            for (info, _) in ecs.iter_resources() {
353                let name = info.name().to_string();
354                writeln!(report, "Resource: {name}").unwrap();
355                // writeln!(report, "- Size: {} bytes",
356                // info.layout().size()).unwrap();
357
358                match name.as_ref() {
359                    "azalea_world::container::Worlds" => {
360                        let worlds = ecs.resource::<Worlds>();
361
362                        for (world_name, world) in &worlds.map {
363                            writeln!(report, "- Name: {world_name}").unwrap();
364                            writeln!(report, "- Reference count: {}", world.strong_count())
365                                .unwrap();
366                            if let Some(world) = world.upgrade() {
367                                let world = world.read();
368                                let chunks = &world.chunks;
369                                let chunks = (chunks as &dyn Any).downcast_ref::<WeakChunkStorage>();
370                                if let Some(chunks) = chunks {
371                                    let strong_chunks = chunks
372                                        .map
373                                        .iter()
374                                        .filter(|(_, v)| v.strong_count() > 0)
375                                        .count();
376                                    writeln!(
377                                        report,
378                                        "- Chunks: {} strongly referenced, {} in map",
379                                        strong_chunks,
380                                        chunks.map.len()
381                                    )
382                                    .unwrap();
383                                }
384                                writeln!(
385                                    report,
386                                    "- Entities: {}",
387                                    world.entities_by_chunk.len()
388                                )
389                                .unwrap();
390                            }
391                        }
392                    }
393                    "bevy_ecs::message::Messages<azalea_client::packet::game::ReceivePacketEvent>" => {
394                        let events = ecs.resource::<Messages<game::ReceiveGamePacketEvent>>();
395                        writeln!(report, "- Event count: {}", events.len()).unwrap();
396                    }
397                    "bevy_ecs::message::Messages<azalea_client::chunks::ReceiveChunkEvent>" => {
398                        let events = ecs.resource::<Messages<ReceiveChunkEvent>>();
399                        writeln!(report, "- Event count: {}", events.len()).unwrap();
400                    }
401
402                    _ => {}
403                }
404            }
405
406            println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
407        });
408
409        1
410    }));
411
412    commands.register(literal("exit").executes(|ctx: &Ctx| {
413        let source = ctx.source.lock();
414        source.reply("bye!");
415
416        source.bot.disconnect();
417
418        let source = ctx.source.clone();
419        thread::spawn(move || {
420            thread::sleep(Duration::from_secs(1));
421
422            source
423                .lock()
424                .bot
425                .ecs
426                .write()
427                .write_message(AppExit::Success);
428        });
429
430        1
431    }));
432}
Source

pub fn get_biome(&self, pos: BlockPos) -> Option<Biome>

Get the biome at the given position.

You can then use Client::with_resolved_registry to get the name and data from the biome.

Note that biomes are internally stored as 4x4x4 blocks, so if you’re writing code that searches for a specific biome it’ll probably be more efficient to avoid scanning every single block.

Source

pub fn set_block_state( &self, pos: BlockPos, state: BlockState, ) -> Option<BlockState>

Trait Implementations§

Source§

impl Debug for World

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for World

Source§

fn default() -> World

Returns the “default value” for a type. Read more
Source§

impl From<ChunkStorage> for World

Source§

fn from(chunks: ChunkStorage) -> Self

Make an empty world from this ChunkStorage. This is meant to be a convenience function for tests.

Auto Trait Implementations§

§

impl Freeze for World

§

impl !RefUnwindSafe for World

§

impl Send for World

§

impl Sync for World

§

impl Unpin for World

§

impl UnsafeUnpin for World

§

impl !UnwindSafe for World

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSend for T
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromWorld for T
where T: Default,

§

fn from_world(_world: &mut World) -> T

Creates Self using default().

§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

§

impl<T> IntoResult<T> for T

§

fn into_result(self) -> Result<T, RunSystemError>

Converts this type into the system output type.
§

impl<A> Is for A
where A: Any,

§

fn is<T>() -> bool
where T: Any,

Checks if the current type “is” another type, using a TypeId equality comparison. This is most useful in the context of generic logic. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

impl<T> ConditionalSend for T
where T: Send,