1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

sort entities_by by distance and improve some docs

This commit is contained in:
mat 2025-06-03 07:40:53 +12:00
parent cee64cece3
commit 5c0e5b1eb3
5 changed files with 73 additions and 15 deletions

View file

@ -1,5 +1,7 @@
use std::{any, sync::Arc}; use std::{any, sync::Arc};
use azalea_core::position::Vec3;
use azalea_entity::Position;
use azalea_world::InstanceName; use azalea_world::InstanceName;
use bevy_ecs::{ use bevy_ecs::{
component::Component, component::Component,
@ -34,13 +36,16 @@ impl Client {
}) })
} }
/// Return a lightweight [`Entity`] for the first entity that matches the /// Return a lightweight [`Entity`] for an arbitrary entity that matches the
/// given predicate function that is in the same [`Instance`] as the /// given predicate function that is in the same [`Instance`] as the
/// client. /// 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.
/// ///
/// Also see [`Self::entities_by`] which will return all entities that match
/// the predicate and sorts them by distance (unlike `entity_by`).
///
/// # Example /// # Example
/// ``` /// ```
/// use azalea_client::{Client, player::GameProfileComponent}; /// use azalea_client::{Client, player::GameProfileComponent};
@ -65,11 +70,14 @@ impl Client {
predicate: impl EntityPredicate<Q, F>, predicate: impl EntityPredicate<Q, F>,
) -> Option<Entity> { ) -> Option<Entity> {
let instance_name = self.get_component::<InstanceName>()?; let instance_name = self.get_component::<InstanceName>()?;
predicate.find(self.ecs.clone(), &instance_name) predicate.find_any(self.ecs.clone(), &instance_name)
} }
/// Same as [`Self::entity_by`] but returns a `Vec<Entity>` of all entities /// Similar to [`Self::entity_by`] but returns a `Vec<Entity>` of all
/// in our instance that match the predicate. /// entities in our instance that match the predicate.
///
/// Unlike `entity_by`, the result is sorted by distance to our client's
/// position, so the closest entity is first.
pub fn entities_by<F: QueryFilter, Q: QueryData>( pub fn entities_by<F: QueryFilter, Q: QueryData>(
&self, &self,
predicate: impl EntityPredicate<Q, F>, predicate: impl EntityPredicate<Q, F>,
@ -77,7 +85,10 @@ impl Client {
let Some(instance_name) = self.get_component::<InstanceName>() else { let Some(instance_name) = self.get_component::<InstanceName>() else {
return vec![]; return vec![];
}; };
predicate.find_all(self.ecs.clone(), &instance_name) let Some(position) = self.get_component::<Position>() else {
return vec![];
};
predicate.find_all_sorted(self.ecs.clone(), &instance_name, (&position).into())
} }
/// 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
@ -109,14 +120,24 @@ impl Client {
} }
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> { pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity>; fn find_any(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName)
fn find_all(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Vec<Entity>; -> Option<Entity>;
fn find_all_sorted(
&self,
ecs_lock: Arc<Mutex<World>>,
instance_name: &InstanceName,
nearest_to: Vec3,
) -> Vec<Entity>;
} }
impl<F, Q: QueryData, Filter: QueryFilter> 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,
{ {
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity> { fn find_any(
&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, &InstanceName, Q), Filter>(); let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
query query
@ -125,13 +146,28 @@ where
.map(|(e, _, _)| e) .map(|(e, _, _)| e)
} }
fn find_all(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Vec<Entity> { fn find_all_sorted(
&self,
ecs_lock: Arc<Mutex<World>>,
instance_name: &InstanceName,
nearest_to: Vec3,
) -> Vec<Entity> {
let mut ecs = ecs_lock.lock(); let mut ecs = ecs_lock.lock();
let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>(); let mut query = ecs.query_filtered::<(Entity, &InstanceName, &Position, Q), Filter>();
query let mut entities = query
.iter(&ecs) .iter(&ecs)
.filter(|(_, e_instance_name, q)| *e_instance_name == instance_name && (self)(q)) .filter(|(_, e_instance_name, _, q)| *e_instance_name == instance_name && (self)(q))
.map(|(e, _, _)| e) .map(|(e, _, position, _)| (e, Vec3::from(position)))
.collect::<Vec<_>>() .collect::<Vec<(Entity, Vec3)>>();
entities.sort_by_cached_key(|(_, position)| {
// to_bits is fine here as long as the number is positive
position.distance_squared_to(&nearest_to).to_bits()
});
entities
.into_iter()
.map(|(e, _)| e)
.collect::<Vec<Entity>>()
} }
} }

View file

@ -8,7 +8,7 @@ use azalea_world::{InstanceContainer, InstanceName};
use bevy_app::{App, Plugin, Update}; use bevy_app::{App, Plugin, Update};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use tracing::{info, trace}; use tracing::trace;
use crate::{ use crate::{
Client, Client,

View file

@ -389,6 +389,8 @@ pub struct Dead;
/// ///
/// This is used to calculate the camera position for players, when spectating /// This is used to calculate the camera position for players, when spectating
/// an entity, and when raycasting from the entity. /// an entity, and when raycasting from the entity.
///
/// The default eye height for a player is 1.62 blocks.
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)] #[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
pub struct EyeHeight(f32); pub struct EyeHeight(f32);
impl EyeHeight { impl EyeHeight {

View file

@ -1883,6 +1883,16 @@ enum IntProviderKind {
} }
registry! { registry! {
/// Every type of item in the game.
///
/// You might find it useful in some cases to check for categories of items
/// with [`azalea_registry::tags::items`](crate::tags::items), like this
///
/// ```
/// let item = azalea_registry::Item::OakLog;
/// let is_log = azalea_registry::tags::items::LOGS.contains(&item);
/// assert!(is_log);
/// ```
enum Item { enum Item {
Air => "minecraft:air", Air => "minecraft:air",
Stone => "minecraft:stone", Stone => "minecraft:stone",

View file

@ -551,6 +551,16 @@ pub fn is_block_state_passable(block: BlockState) -> bool {
return false; return false;
} }
if registry_block == azalea_registry::Block::PowderSnow {
// we can't jump out of powder snow
return false;
}
if registry_block == azalea_registry::Block::SweetBerryBush {
// these hurt us
return false;
}
true true
} }