bevy/crates/bevy_core/src/name.rs
Mark Wainwright f0a8994f55
Split WorldQuery into WorldQueryData and WorldQueryFilter (#9918)
# Objective

- Fixes #7680
- This is an updated for https://github.com/bevyengine/bevy/pull/8899
which had the same objective but fell a long way behind the latest
changes


## Solution

The traits `WorldQueryData : WorldQuery` and `WorldQueryFilter :
WorldQuery` have been added and some of the types and functions from
`WorldQuery` has been moved into them.

`ReadOnlyWorldQuery` has been replaced with `ReadOnlyWorldQueryData`. 

`WorldQueryFilter` is safe (as long as `WorldQuery` is implemented
safely).

`WorldQueryData` is unsafe - safely implementing it requires that
`Self::ReadOnly` is a readonly version of `Self` (this used to be a
safety requirement of `WorldQuery`)

The type parameters `Q` and `F` of `Query` must now implement
`WorldQueryData` and `WorldQueryFilter` respectively.

This makes it impossible to accidentally use a filter in the data
position or vice versa which was something that could lead to bugs.
~~Compile failure tests have been added to check this.~~

It was previously sometimes useful to use `Option<With<T>>` in the data
position. Use `Has<T>` instead in these cases.

The `WorldQuery` derive macro has been split into separate derive macros
for `WorldQueryData` and `WorldQueryFilter`.

Previously it was possible to derive both `WorldQuery` for a struct that
had a mixture of data and filter items. This would not work correctly in
some cases but could be a useful pattern in others. *This is no longer
possible.*

---

## Notes

- The changes outside of `bevy_ecs` are all changing type parameters to
the new types, updating the macro use, or replacing `Option<With<T>>`
with `Has<T>`.

- All `WorldQueryData` types always returned `true` for `IS_ARCHETYPAL`
so I moved it to `WorldQueryFilter` and
replaced all calls to it with `true`. That should be the only logic
change outside of the macro generation code.

- `Changed<T>` and `Added<T>` were being generated by a macro that I
have expanded. Happy to revert that if desired.

- The two derive macros share some functions for implementing
`WorldQuery` but the tidiest way I could find to implement them was to
give them a ton of arguments and ask clippy to ignore that.

## Changelog

### Changed
- Split `WorldQuery` into `WorldQueryData` and `WorldQueryFilter` which
now have separate derive macros. It is not possible to derive both for
the same type.
- `Query` now requires that the first type argument implements
`WorldQueryData` and the second implements `WorldQueryFilter`

## Migration Guide

- Update derives

```rust
// old
#[derive(WorldQuery)]
#[world_query(mutable, derive(Debug))]
struct CustomQuery {
    entity: Entity,
    a: &'static mut ComponentA
}

#[derive(WorldQuery)]
struct QueryFilter {
    _c: With<ComponentC>
}

// new 
#[derive(WorldQueryData)]
#[world_query_data(mutable, derive(Debug))]
struct CustomQuery {
    entity: Entity,
    a: &'static mut ComponentA,
}

#[derive(WorldQueryFilter)]
struct QueryFilter {
    _c: With<ComponentC>
}
```
- Replace `Option<With<T>>` with `Has<T>`

```rust
/// old
fn my_system(query: Query<(Entity, Option<With<ComponentA>>)>)
{
  for (entity, has_a_option) in query.iter(){
    let has_a:bool = has_a_option.is_some();
    //todo!()
  }
}

/// new
fn my_system(query: Query<(Entity, Has<ComponentA>)>)
{
  for (entity, has_a) in query.iter(){
    //todo!()
  }
}
```

- Fix queries which had filters in the data position or vice versa.

```rust
// old
fn my_system(query: Query<(Entity, With<ComponentA>)>)
{
  for (entity, _) in query.iter(){
  //todo!()
  }
}

// new
fn my_system(query: Query<Entity, With<ComponentA>>)
{
  for entity in query.iter(){
  //todo!()
  }
}

// old
fn my_system(query: Query<AnyOf<(&ComponentA, With<ComponentB>)>>)
{
  for (entity, _) in query.iter(){
  //todo!()
  }
}

// new
fn my_system(query: Query<Option<&ComponentA>, Or<(With<ComponentA>, With<ComponentB>)>>)
{
  for entity in query.iter(){
  //todo!()
  }
}

```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2023-11-28 03:56:07 +00:00

196 lines
4.8 KiB
Rust

use bevy_ecs::query::WorldQueryData;
use bevy_ecs::{component::Component, entity::Entity, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_utils::AHasher;
use std::{
borrow::Cow,
hash::{Hash, Hasher},
ops::Deref,
};
/// Component used to identify an entity. Stores a hash for faster comparisons.
///
/// The hash is eagerly re-computed upon each update to the name.
///
/// [`Name`] should not be treated as a globally unique identifier for entities,
/// as multiple entities can have the same name. [`bevy_ecs::entity::Entity`] should be
/// used instead as the default unique identifier.
#[derive(Reflect, Component, Clone)]
#[reflect(Component, Default, Debug)]
pub struct Name {
hash: u64, // TODO: Shouldn't be serialized
name: Cow<'static, str>,
}
impl Default for Name {
fn default() -> Self {
Name::new("")
}
}
impl Name {
/// Creates a new [`Name`] from any string-like type.
///
/// The internal hash will be computed immediately.
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
let name = name.into();
let mut name = Name { name, hash: 0 };
name.update_hash();
name
}
/// Sets the entity's name.
///
/// The internal hash will be re-computed.
#[inline(always)]
pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
*self = Name::new(name);
}
/// Updates the name of the entity in place.
///
/// This will allocate a new string if the name was previously
/// created from a borrow.
#[inline(always)]
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
f(self.name.to_mut());
self.update_hash();
}
/// Gets the name of the entity as a `&str`.
#[inline(always)]
pub fn as_str(&self) -> &str {
&self.name
}
fn update_hash(&mut self) {
let mut hasher = AHasher::default();
self.name.hash(&mut hasher);
self.hash = hasher.finish();
}
}
impl std::fmt::Display for Name {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.name, f)
}
}
impl std::fmt::Debug for Name {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.name, f)
}
}
/// Convenient query for giving a human friendly name to an entity.
///
/// ```rust
/// # use bevy_core::prelude::*;
/// # use bevy_ecs::prelude::*;
/// # #[derive(Component)] pub struct Score(f32);
/// fn increment_score(mut scores: Query<(DebugName, &mut Score)>) {
/// for (name, mut score) in &mut scores {
/// score.0 += 1.0;
/// if score.0.is_nan() {
/// bevy_utils::tracing::error!("Score for {:?} is invalid", name);
/// }
/// }
/// }
/// # bevy_ecs::system::assert_is_system(increment_score);
/// ```
#[derive(WorldQueryData)]
pub struct DebugName {
/// A [`Name`] that the entity might have that is displayed if available.
pub name: Option<&'static Name>,
/// The unique identifier of the entity as a fallback.
pub entity: Entity,
}
impl<'a> std::fmt::Debug for DebugNameItem<'a> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.name {
Some(name) => write!(f, "{:?} ({:?})", &name, &self.entity),
None => std::fmt::Debug::fmt(&self.entity, f),
}
}
}
/* Conversions from strings */
impl From<&str> for Name {
#[inline(always)]
fn from(name: &str) -> Self {
Name::new(name.to_owned())
}
}
impl From<String> for Name {
#[inline(always)]
fn from(name: String) -> Self {
Name::new(name)
}
}
/* Conversions to strings */
impl AsRef<str> for Name {
#[inline(always)]
fn as_ref(&self) -> &str {
&self.name
}
}
impl From<&Name> for String {
#[inline(always)]
fn from(val: &Name) -> String {
val.as_str().to_owned()
}
}
impl From<Name> for String {
#[inline(always)]
fn from(val: Name) -> String {
val.name.into_owned()
}
}
impl Hash for Name {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
if self.hash != other.hash {
// Makes the common case of two strings not been equal very fast
return false;
}
self.name.eq(&other.name)
}
}
impl Eq for Name {}
impl PartialOrd for Name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.name.cmp(&other.name)
}
}
impl Deref for Name {
type Target = str;
fn deref(&self) -> &Self::Target {
self.name.as_ref()
}
}