mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 23:44:38 +00:00
add Client::entities_by and improve some docs
This commit is contained in:
parent
d028d7c3e9
commit
1d3a7c969f
5 changed files with 63 additions and 36 deletions
|
@ -1,5 +1,6 @@
|
||||||
use std::{any, sync::Arc};
|
use std::{any, sync::Arc};
|
||||||
|
|
||||||
|
use azalea_world::InstanceName;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
|
@ -33,14 +34,14 @@ impl Client {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a lightweight [`Entity`] for the entity that matches the given
|
/// Return a lightweight [`Entity`] for the first entity that matches the
|
||||||
/// predicate function.
|
/// given predicate function that is in the same [`Instance`] as the
|
||||||
|
/// client.
|
||||||
///
|
///
|
||||||
/// You can then use [`Self::entity_component`] to get components from this
|
/// You can then use [`Self::entity_component`] to get components from this
|
||||||
/// entity.
|
/// entity.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// Note that this will very likely change in the future.
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use azalea_client::{Client, GameProfileComponent};
|
/// use azalea_client::{Client, GameProfileComponent};
|
||||||
/// use azalea_entity::{Position, metadata::Player};
|
/// use azalea_entity::{Position, metadata::Player};
|
||||||
|
@ -58,11 +59,25 @@ impl Client {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Entity`]: bevy_ecs::entity::Entity
|
/// [`Entity`]: bevy_ecs::entity::Entity
|
||||||
|
/// [`Instance`]: azalea_world::Instance
|
||||||
pub fn entity_by<F: QueryFilter, Q: QueryData>(
|
pub fn entity_by<F: QueryFilter, Q: QueryData>(
|
||||||
&self,
|
&self,
|
||||||
predicate: impl EntityPredicate<Q, F>,
|
predicate: impl EntityPredicate<Q, F>,
|
||||||
) -> Option<Entity> {
|
) -> Option<Entity> {
|
||||||
predicate.find(self.ecs.clone())
|
let instance_name = self.get_component::<InstanceName>()?;
|
||||||
|
predicate.find(self.ecs.clone(), &instance_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`Self::entity_by`] but returns a `Vec<Entity>` of all entities
|
||||||
|
/// in our instance that match the predicate.
|
||||||
|
pub fn entities_by<F: QueryFilter, Q: QueryData>(
|
||||||
|
&self,
|
||||||
|
predicate: impl EntityPredicate<Q, F>,
|
||||||
|
) -> Vec<Entity> {
|
||||||
|
let Some(instance_name) = self.get_component::<InstanceName>() 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
|
/// Get a component from an entity. Note that this will return an owned type
|
||||||
|
@ -94,35 +109,37 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
|
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
|
||||||
fn find(&self, ecs_lock: Arc<Mutex<World>>) -> Option<Entity>;
|
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity>;
|
||||||
|
fn find_all<'a>(
|
||||||
|
&'a self,
|
||||||
|
ecs_lock: Arc<Mutex<World>>,
|
||||||
|
instance_name: &InstanceName,
|
||||||
|
) -> Vec<Entity>;
|
||||||
}
|
}
|
||||||
impl<F, Q, Filter> EntityPredicate<Q, Filter> for F
|
impl<F, Q: QueryData, Filter: QueryFilter> EntityPredicate<Q, Filter> for F
|
||||||
where
|
where
|
||||||
F: Fn(&ROQueryItem<Q>) -> bool,
|
F: Fn(&ROQueryItem<Q>) -> bool,
|
||||||
Q: QueryData,
|
|
||||||
Filter: QueryFilter,
|
|
||||||
{
|
{
|
||||||
fn find(&self, ecs_lock: Arc<Mutex<World>>) -> Option<Entity> {
|
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity> {
|
||||||
let mut ecs = ecs_lock.lock();
|
let mut ecs = ecs_lock.lock();
|
||||||
let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
|
let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
|
||||||
query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e)
|
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<Mutex<World>>,
|
||||||
|
instance_name: &InstanceName,
|
||||||
|
) -> Vec<Entity> {
|
||||||
|
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::<Vec<_>>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<'a, F, Q1, Q2> EntityPredicate<'a, (Q1, Q2)> for F
|
|
||||||
// where
|
|
||||||
// F: Fn(&<Q1 as WorldQuery>::Item<'_>, &<Q2 as WorldQuery>::Item<'_>) ->
|
|
||||||
// bool, Q1: QueryFilter,
|
|
||||||
// Q2: QueryFilter,
|
|
||||||
// {
|
|
||||||
// fn find(&self, ecs: &mut Ecs) -> Option<Entity> {
|
|
||||||
// // (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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -124,9 +124,9 @@ impl ChatPacket {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this message was sent with /msg (or aliases). It works by
|
/// Whether this message is an incoming whisper message (i.e. someone else
|
||||||
/// checking the translation key, so it won't work on servers that use their
|
/// dm'd the bot with /msg). It works by checking the translation key, so it
|
||||||
/// own whisper system.
|
/// won't work on servers that use their own whisper system.
|
||||||
pub fn is_whisper(&self) -> bool {
|
pub fn is_whisper(&self) -> bool {
|
||||||
match self.message() {
|
match self.message() {
|
||||||
FormattedText::Text(_) => false,
|
FormattedText::Text(_) => false,
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl InstanceContainer {
|
||||||
let existing = existing_lock.read();
|
let existing = existing_lock.read();
|
||||||
if existing.chunks.height != height {
|
if existing.chunks.height != height {
|
||||||
error!(
|
error!(
|
||||||
"Shared dimension height mismatch: {} != {height}",
|
"Shared world height mismatch: {} != {height}",
|
||||||
existing.chunks.height
|
existing.chunks.height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ impl InstanceContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The name of the [`Instance`](crate::Instance) (world) the entity is
|
/// 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
|
/// in. If two entities share the same instance name, we assume they're in the
|
||||||
/// instance.
|
/// same instance.
|
||||||
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
||||||
#[doc(alias("worldname", "world name"))]
|
#[doc(alias("worldname", "world name"))]
|
||||||
pub struct InstanceName(pub ResourceLocation);
|
pub struct InstanceName(pub ResourceLocation);
|
||||||
|
|
|
@ -144,6 +144,14 @@ impl PartialEntityInfos {
|
||||||
|
|
||||||
/// A world where the chunks are stored as weak pointers. This is used for
|
/// A world where the chunks are stored as weak pointers. This is used for
|
||||||
/// shared worlds.
|
/// 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)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub chunks: ChunkStorage,
|
pub chunks: ChunkStorage,
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub struct GotoEvent {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub goal: Arc<dyn Goal>,
|
pub goal: Arc<dyn Goal>,
|
||||||
/// The function that's used for checking what moves are possible. Usually
|
/// The function that's used for checking what moves are possible. Usually
|
||||||
/// `pathfinder::moves::default_move`
|
/// [`moves::default_move`].
|
||||||
pub successors_fn: SuccessorsFn,
|
pub successors_fn: SuccessorsFn,
|
||||||
|
|
||||||
/// Whether the bot is allowed to break blocks while pathfinding.
|
/// 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
|
/// Pathfind to the given goal and wait until either the target is reached
|
||||||
/// or the pathfinding is canceled.
|
/// or the pathfinding is canceled.
|
||||||
///
|
///
|
||||||
|
/// You can use [`Self::start_goto`] instead if you don't want to wait.
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use azalea::prelude::*;
|
/// # use azalea::prelude::*;
|
||||||
/// # use azalea::{BlockPos, pathfinder::goals::BlockPosGoal};
|
/// # use azalea::{BlockPos, pathfinder::goals::BlockPosGoal};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue