diff --git a/azalea-client/src/entity_query.rs b/azalea-client/src/entity_query.rs index 1f2ff080..409253a3 100644 --- a/azalea-client/src/entity_query.rs +++ b/azalea-client/src/entity_query.rs @@ -1,5 +1,6 @@ use std::{any, sync::Arc}; +use azalea_world::InstanceName; use bevy_ecs::{ component::Component, entity::Entity, @@ -33,14 +34,14 @@ impl Client { }) } - /// Return a lightweight [`Entity`] for the entity that matches the given - /// predicate function. + /// Return a lightweight [`Entity`] for the first 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. /// /// # Example - /// Note that this will very likely change in the future. /// ``` /// use azalea_client::{Client, GameProfileComponent}; /// use azalea_entity::{Position, metadata::Player}; @@ -58,11 +59,25 @@ impl Client { /// ``` /// /// [`Entity`]: bevy_ecs::entity::Entity + /// [`Instance`]: azalea_world::Instance pub fn entity_by( &self, predicate: impl EntityPredicate, ) -> Option { - predicate.find(self.ecs.clone()) + let instance_name = self.get_component::()?; + predicate.find(self.ecs.clone(), &instance_name) + } + + /// Same as [`Self::entity_by`] but returns a `Vec` of all entities + /// in our instance that match the predicate. + pub fn entities_by( + &self, + predicate: impl EntityPredicate, + ) -> Vec { + let Some(instance_name) = self.get_component::() else { + return vec![]; + }; + predicate.find_all(self.ecs.clone(), &instance_name) } /// Get a component from an entity. Note that this will return an owned type @@ -94,35 +109,37 @@ impl Client { } pub trait EntityPredicate { - fn find(&self, ecs_lock: Arc>) -> Option; + fn find(&self, ecs_lock: Arc>, instance_name: &InstanceName) -> Option; + fn find_all<'a>( + &'a self, + ecs_lock: Arc>, + instance_name: &InstanceName, + ) -> Vec; } -impl EntityPredicate for F +impl EntityPredicate for F where F: Fn(&ROQueryItem) -> bool, - Q: QueryData, - Filter: QueryFilter, { - fn find(&self, ecs_lock: Arc>) -> Option { + fn find(&self, ecs_lock: Arc>, instance_name: &InstanceName) -> Option { let mut ecs = ecs_lock.lock(); - let mut query = ecs.query_filtered::<(Entity, Q), Filter>(); - query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e) + let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>(); + query + .iter(&ecs) + .find(|(_, e_instance_name, q)| *e_instance_name == instance_name && (self)(q)) + .map(|(e, _, _)| e) + } + + fn find_all<'a>( + &'a self, + ecs_lock: Arc>, + instance_name: &InstanceName, + ) -> Vec { + let mut ecs = ecs_lock.lock(); + let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>(); + query + .iter(&ecs) + .filter(|(_, e_instance_name, q)| *e_instance_name == instance_name && (self)(q)) + .map(|(e, _, _)| e) + .collect::>() } } - -// impl<'a, F, Q1, Q2> EntityPredicate<'a, (Q1, Q2)> for F -// where -// F: Fn(&::Item<'_>, &::Item<'_>) -> -// bool, Q1: QueryFilter, -// Q2: QueryFilter, -// { -// fn find(&self, ecs: &mut Ecs) -> Option { -// // (self)(query) -// let mut query = ecs.query_filtered::<(Entity, Q1, Q2), ()>(); -// let entity = query -// .iter(ecs) -// .find(|(_, q1, q2)| (self)(q1, q2)) -// .map(|(e, _, _)| e); - -// entity -// } -// } diff --git a/azalea-client/src/plugins/chat/mod.rs b/azalea-client/src/plugins/chat/mod.rs index 7d42eb20..603dea60 100644 --- a/azalea-client/src/plugins/chat/mod.rs +++ b/azalea-client/src/plugins/chat/mod.rs @@ -124,9 +124,9 @@ impl ChatPacket { })) } - /// Whether this message was sent with /msg (or aliases). It works by - /// checking the translation key, so it won't work on servers that use their - /// own whisper system. + /// Whether this message is an incoming whisper message (i.e. someone else + /// dm'd the bot with /msg). It works by checking the translation key, so it + /// won't work on servers that use their own whisper system. pub fn is_whisper(&self) -> bool { match self.message() { FormattedText::Text(_) => false, diff --git a/azalea-world/src/container.rs b/azalea-world/src/container.rs index 53b9c784..bb8ade70 100644 --- a/azalea-world/src/container.rs +++ b/azalea-world/src/container.rs @@ -58,7 +58,7 @@ impl InstanceContainer { let existing = existing_lock.read(); if existing.chunks.height != height { error!( - "Shared dimension height mismatch: {} != {height}", + "Shared world height mismatch: {} != {height}", existing.chunks.height ); } @@ -86,8 +86,8 @@ impl InstanceContainer { } /// The name of the [`Instance`](crate::Instance) (world) the entity is -/// in. If two entities share the same world name, we assume they're in the same -/// instance. +/// in. If two entities share the same instance name, we assume they're in the +/// same instance. #[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)] #[doc(alias("worldname", "world name"))] pub struct InstanceName(pub ResourceLocation); diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index 00bee13a..47804dcc 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -144,6 +144,14 @@ impl PartialEntityInfos { /// A world where the chunks are stored as weak pointers. This is used for /// shared worlds. +/// +/// Also see [`PartialInstance`]. +/// +/// The reason this is called "instance" instead of "world" or "dimension" is +/// because "world" already means the entire ECS (which can contain multiple +/// instances if we're in a swarm) and "dimension" can be ambiguous (for +/// instance there can be multiple overworlds, and "dimension" is also a math +/// term) #[derive(Default, Debug)] pub struct Instance { pub chunks: ChunkStorage, diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 1b8a71c8..12b6f156 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -142,7 +142,7 @@ pub struct GotoEvent { pub entity: Entity, pub goal: Arc, /// The function that's used for checking what moves are possible. Usually - /// `pathfinder::moves::default_move` + /// [`moves::default_move`]. pub successors_fn: SuccessorsFn, /// Whether the bot is allowed to break blocks while pathfinding. @@ -198,6 +198,8 @@ impl PathfinderClientExt for azalea_client::Client { /// Pathfind to the given goal and wait until either the target is reached /// or the pathfinding is canceled. /// + /// You can use [`Self::start_goto`] instead if you don't want to wait. + /// /// ``` /// # use azalea::prelude::*; /// # use azalea::{BlockPos, pathfinder::goals::BlockPosGoal};