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.

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

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

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 !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,