From 8ab990d98ec36ee893dfcb6030de272ea1375e2c Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Fri, 5 Apr 2024 09:32:20 -0700 Subject: [PATCH] Redesign `ComponentAccessExpr`, improve error diagnostics (#35) This PR redesigns the `ComponentAccessExpr` type to track access conflicts more precisely. Queries such as `Or), (&A, Not<&B>)>, (&A, Not<&B>)>` and `(&mut A, &mut A, Not<&A>)` will now pass the component access checker. Additionally, we now defer access errors until after handler init has finished. This improves diagnostics by showing a list of all access conflicts together. ## TODO - [x] Clean up warnings --- CHANGELOG.md | 7 + evenio_macros/src/handler_param.rs | 2 +- evenio_macros/src/query.rs | 4 +- src/access.rs | 513 +++++++++++++-------------- src/archetype.rs | 18 +- src/bit_set.rs | 72 +++- src/bool_expr.rs | 302 ---------------- src/component.rs | 4 +- src/entity.rs | 4 +- src/event.rs | 134 ++----- src/fetch.rs | 31 +- src/handler.rs | 289 ++++++++++----- src/lib.rs | 3 +- src/query.rs | 168 ++++----- src/world.rs | 178 +++++++--- tutorial/ch01_handlers_and_events.md | 4 +- 16 files changed, 761 insertions(+), 972 deletions(-) delete mode 100644 src/bool_expr.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0327b60..9662318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## Unreleased + +- Redesign component access checking. Complex queries will now pass the component access checker in more cases. +- Improve diagnostics for component access errors. +- Redesign `Config` interface to hide implementation details. (Now named `HandlerConfig`). +- Removed `bit_set` module from public API. + ## 0.4.2 - 2024-03-24 - Fixed bug in entity despawning. diff --git a/evenio_macros/src/handler_param.rs b/evenio_macros/src/handler_param.rs index 222ce08..cbd5dac 100644 --- a/evenio_macros/src/handler_param.rs +++ b/evenio_macros/src/handler_param.rs @@ -122,7 +122,7 @@ pub(crate) fn derive_handler_param(input: TokenStream) -> Result { fn init( world: &mut ::evenio::world::World, - config: &mut ::evenio::handler::Config, + config: &mut ::evenio::handler::HandlerConfig, ) -> ::core::result::Result { <#tuple_ty as ::evenio::handler::HandlerParam>::init(world, config) diff --git a/evenio_macros/src/query.rs b/evenio_macros/src/query.rs index ba45dbe..c29a525 100644 --- a/evenio_macros/src/query.rs +++ b/evenio_macros/src/query.rs @@ -122,8 +122,8 @@ pub(crate) fn derive_query(input: TokenStream) -> Result { fn init( world: &mut ::evenio::world::World, - config: &mut ::evenio::handler::Config - ) -> ::core::result::Result<(::evenio::access::ComponentAccessExpr, Self::State), ::evenio::handler::InitError> + config: &mut ::evenio::handler::HandlerConfig + ) -> ::core::result::Result<(::evenio::access::ComponentAccess, Self::State), ::evenio::handler::InitError> { <#tuple_ty as ::evenio::query::Query>::init(world, config) } diff --git a/src/access.rs b/src/access.rs index ef930b9..5484650 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,12 +1,13 @@ //! Data access checking. +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::cmp::Ordering; -use core::fmt; -use crate::bit_set::BitSet; -use crate::bool_expr::BoolExpr; +use ahash::RandomState; + use crate::component::ComponentIdx; -use crate::sparse::SparseIndex; +use crate::map::IndexSet; /// Describes how a particular piece of data is accessed. Used to prevent /// aliased mutabliity. @@ -22,15 +23,43 @@ pub enum Access { } impl Access { - /// Can `self` and `other` be active at the same time without causing - /// aliased mutability? + /// Produces the access which is the superset of `self` and `other`. If the + /// two accesses are incompatible with each other, then `None` is returned + /// instead. + /// + /// # Examples + /// + /// ``` + /// use evenio::access::Access; + /// + /// assert_eq!(Access::Read.join(Access::Read), Some(Access::Read)); + /// assert_eq!(Access::Read.join(Access::ReadWrite), None); + /// assert_eq!( + /// Access::ReadWrite.join(Access::None), + /// Some(Access::ReadWrite) + /// ); + /// ``` + #[must_use] + pub const fn join(self, other: Self) -> Option { + match (self, other) { + (Access::None, Access::None) => Some(Access::None), + (Access::Read | Access::ReadWrite, Access::None) => Some(self), + (Access::None, Access::Read | Access::ReadWrite) => Some(other), + (Access::Read, Access::Read) => Some(Access::Read), + (Access::Read | Access::ReadWrite, Access::ReadWrite) + | (Access::ReadWrite, Access::Read) => None, + } + } + + /// Shorthand for `self.join(other).is_some()`. See [`join`] for more + /// information. /// - /// This operation is symmetric. + /// [`join`]: Self::join /// /// # Examples /// - /// ```rust - /// use evenio::access::Access::*; + /// ``` + /// use evenio::access::Access::{None, Read, ReadWrite}; /// /// assert!(Read.is_compatible(Read)); /// assert!(!Read.is_compatible(ReadWrite)); @@ -38,255 +67,229 @@ impl Access { /// ``` #[must_use] pub const fn is_compatible(self, other: Self) -> bool { - matches!( - (self, other), - (Access::None, _) - | (Access::Read, Access::None | Access::Read) - | (Access::ReadWrite, Access::None) - ) + self.join(other).is_some() } +} - /// Sets `self` equal to `other` if `self` is [compatible] with `other`. - /// Returns whether or not the assignment occurred. +/// An expression describing the components accessed by a query. +/// +/// This is used for checking that queries don't break aliasing rules, as well +/// as determining which archetypes are matched by a query. +#[must_use] +#[derive(Clone, Debug)] +pub struct ComponentAccess { + /// The set of cases that need to be considered for access checking. /// - /// # Examples + /// If any of the cases end up with a [`CaseAccess::Conflict`], then we know + /// the query is invalid. /// - /// ```rust - /// use evenio::access::Access; + /// Example: The query `Or<&A, &mut A>` has three cases. + /// 1. `&A` left branch. + /// 2. `&mut A` right branch. + /// 3. `(&A, &mut A)` both branches. `&A` and `&mut A` are merged together + /// to form a conflict in `A`. /// - /// let mut access = Access::None; + /// Since case (3) has a conflict, we know the whole query is invalid. /// - /// assert!(access.set_if_compatible(Access::ReadWrite)); - /// assert_eq!(access, Access::ReadWrite); - /// ``` + /// This vec can be viewed as the "lists of lists" needed for + /// [Disjunctive Normal Form][dnf], but with some necessary modifications in + /// order to track access conflicts. /// - /// [compatible]: Access::is_compatible - #[must_use] - pub fn set_if_compatible(&mut self, other: Self) -> bool { - if self.is_compatible(other) { - *self = other; - true - } else { - false - } - } + /// [dnf]: https://en.wikipedia.org/wiki/Disjunctive_normal_form + cases: Vec, } -/// Map from `T` to [`Access`] with sparse index keys. All keys map to -/// [`Access::None`] by default. -pub struct AccessMap { - read: BitSet, - write: BitSet, -} +/// Association list from component to access. Sorted in ascending order by +/// [`ComponentIdx`]. +type Case = Vec<(ComponentIdx, CaseAccess)>; -impl AccessMap { - /// Creates an empty access map. - pub fn new() -> Self { - Self { - read: BitSet::new(), - write: BitSet::new(), - } - } - - /// Gets the access for `key`. - pub fn get(&self, key: T) -> Access - where - T: SparseIndex, - { - match (self.read.contains(key), self.write.contains(key)) { - (true, true) => Access::ReadWrite, - (true, false) => Access::Read, - (false, true) => unreachable!("read-write implies read"), - (false, false) => Access::None, - } - } - - /// Sets the access for `key`. - pub fn set(&mut self, key: T, access: Access) - where - T: SparseIndex, - { - match access { - Access::None => { - self.read.remove(key); - self.write.remove(key); - } - Access::Read => { - self.read.insert(key); - self.write.remove(key); - } - Access::ReadWrite => { - self.read.insert(key); - self.write.insert(key); - } - } - } - - /// Clears the access map. All keys will map to [`Access::None`]. - pub fn clear(&mut self) { - self.read.clear(); - self.write.clear(); - } - - /// Returns whether all values in `self` can be active at the same time as - /// all values in `other`. - pub fn is_compatible(&self, other: &Self) -> bool { - self.read.is_disjoint(&other.write) && self.write.is_disjoint(&other.read) - } - - /// Computes the union between `self` and `other` and assigns the result to - /// `self`. - pub fn union_assign(&mut self, other: &Self) { - self.read |= &other.read; - self.write |= &other.write; - } -} - -impl Default for AccessMap { - fn default() -> Self { - Self::new() - } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum CaseAccess { + /// Archetype must have the component, but the component is not read or + /// written. + With, + /// Component is read. + Read, + /// Component is read and/or written. + ReadWrite, + /// Archetype must not have the component. + Not, + /// Access to component violates mutable aliasing. + Conflict, } -impl Clone for AccessMap { - fn clone(&self) -> Self { +impl ComponentAccess { + /// Create a new `ComponentAccess` which matches all archetypes but has no + /// access. + pub fn new_true() -> Self { Self { - read: self.read.clone(), - write: self.write.clone(), + cases: vec![vec![]], } } -} - -impl Ord for AccessMap { - fn cmp(&self, other: &Self) -> Ordering { - self.read - .cmp(&other.read) - .then_with(|| self.write.cmp(&other.write)) - } -} -impl PartialOrd for AccessMap { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + /// Create a new `ComponentAccess` which matches no archetypes and has no + /// access. + pub fn new_false() -> Self { + Self { cases: vec![] } } -} -impl PartialEq for AccessMap { - fn eq(&self, other: &Self) -> bool { - self.cmp(other).is_eq() - } -} - -impl Eq for AccessMap {} - -impl fmt::Debug for AccessMap -where - T: SparseIndex + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AccessMap") - .field("read", &self.read) - .field("write", &self.write) - .finish() - } -} - -/// The combination of a [`BoolExpr`] with an [`AccessMap`]. -/// -/// This is capable of precisely describing the components accessed by a query, -/// e.g. `(&A, &mut B)`. -#[derive(Clone, Debug)] -pub struct ComponentAccessExpr { - /// The boolean expression part. - pub expr: BoolExpr, - /// The access map part. - pub access: AccessMap, -} - -#[allow(clippy::should_implement_trait)] -impl ComponentAccessExpr { - /// Creates a new component access expr corresponding to `true` or `false`. - /// The access map is initialized empty. - pub fn new(b: bool) -> Self { + /// Create a new `ComponentAccess` which accesses a single component. + pub fn var(idx: ComponentIdx, access: Access) -> Self { Self { - expr: BoolExpr::new(b), - access: AccessMap::new(), + cases: vec![vec![( + idx, + match access { + Access::None => CaseAccess::With, + Access::Read => CaseAccess::Read, + Access::ReadWrite => CaseAccess::ReadWrite, + }, + )]], } } - /// Creates a new component access expr which accesses a single component, - /// e.g. `&A` or `&mut A`. - pub fn with(component: ComponentIdx, access: Access) -> Self { - let mut map = AccessMap::new(); - map.set(component, access); - - Self { - expr: BoolExpr::var(component), - access: map, + /// Logically AND two access expressions together.i + pub fn and(&self, rhs: &Self) -> Self { + let mut cases = vec![]; + + for right in &rhs.cases { + 'next_case: for left in &self.cases { + let mut case = vec![]; + let mut il = 0; + let mut ir = 0; + + loop { + match (left.get(il), right.get(ir)) { + (None, None) => break, + (None, Some(_)) => { + case.extend(right[ir..].iter().copied()); + break; + } + (Some(_), None) => { + case.extend(left[il..].iter().copied()); + break; + } + (Some(&(left_idx, left_access)), Some(&(right_idx, right_access))) => { + match left_idx.cmp(&right_idx) { + Ordering::Less => { + il += 1; + case.push((left_idx, left_access)); + } + Ordering::Equal => { + il += 1; + ir += 1; + use CaseAccess::*; + let combined_access = match (left_access, right_access) { + (With | Read, Read) | (Read, With) => Read, + (With, ReadWrite) | (ReadWrite, With) => ReadWrite, + (With, With) => With, + (Not, Not) => Not, + (Not, With | Read | ReadWrite | Conflict) + | (With | Read | ReadWrite | Conflict, Not) => { + // Skip this case since both accessing and not accessing + // the same component is impossible. + continue 'next_case; + } + (Conflict, With | Read | ReadWrite | Conflict) + | (With | Read | ReadWrite, Conflict) + | (Read | ReadWrite, ReadWrite) + | (ReadWrite, Read) => Conflict, + }; + case.push((left_idx, combined_access)); + } + Ordering::Greater => { + ir += 1; + case.push((right_idx, right_access)); + } + } + } + } + } + + cases.push(case); + } } + + Self { cases } } - /// Creates a new component access expr which does not access a single - /// component, e.g. `Not`. - pub fn without(component: ComponentIdx) -> Self { + /// Logically OR two access expressions together. + pub fn or(&self, rhs: &Self) -> Self { Self { - expr: BoolExpr::not_var(component), - access: AccessMap::new(), + cases: self.cases.iter().chain(rhs.cases.iter()).cloned().collect(), } } - /// Checks if `self` can be active as the same time as `other` without - /// causing access conflicts. - pub fn is_compatible(&self, other: &Self) -> bool { - if self.access.is_compatible(&other.access) { - return true; - } - - // The component accesses of `self` and `other` are incompatible. However, if - // the accesses don't overlap, then there's no incompatibility. - // - // For instance, `(&mut A, &B)` and `(&mut A, Not<&B>)` are incompatible with - // respect to `A`, but are disjoint so there's no overlap. - self.expr.is_disjoint(&other.expr) + /// Inverts the accesses according to De Morgan's laws. + /// + /// This that this is a lossy operation because read/write information is + /// lost. + pub fn not(&self) -> Self { + self.cases + .iter() + .map(|case| Self { + cases: case + .iter() + .map(|&(idx, access)| { + let new_access = match access { + CaseAccess::With => CaseAccess::Not, + CaseAccess::Read => CaseAccess::Not, + CaseAccess::ReadWrite => CaseAccess::Not, + CaseAccess::Not => CaseAccess::With, + CaseAccess::Conflict => CaseAccess::Not, + }; + + vec![(idx, new_access)] + }) + .collect(), + }) + .fold(Self::new_true(), |acc, item| acc.and(&item)) } - /// ANDs two access exprs together. Returns `Err` if the two exprs are - /// incompatible. - pub fn and(mut self, other: &Self) -> Result { - if !self.is_compatible(other) { - return Err(self); + /// Clears all component access without altering the matched archetypes. + /// Equivalent to `self = self.not().not()`. + pub fn clear_access(&mut self) { + for case in &mut self.cases { + for (_, access) in case { + *access = match *access { + CaseAccess::With => CaseAccess::With, + CaseAccess::Read => CaseAccess::With, + CaseAccess::ReadWrite => CaseAccess::With, + CaseAccess::Not => CaseAccess::Not, + CaseAccess::Conflict => CaseAccess::With, + } + } } - - self.access.union_assign(&other.access); - self.expr = self.expr.and(&other.expr); - Ok(self) } - /// ORs two access exprs together. Returns `Err` if the two exprs are - /// incompatible. - pub fn or(mut self, other: &Self) -> Result { - if !self.is_compatible(other) { - return Err(self); + /// Returns the set of all conflicting components. + pub(crate) fn collect_conflicts(&self) -> IndexSet { + let mut res = IndexSet::with_hasher(RandomState::new()); + + for case in &self.cases { + for &(idx, access) in case { + if access == CaseAccess::Conflict { + res.insert(idx); + } + } } - self.access.union_assign(&other.access); - self.expr = self.expr.or(&other.expr); - Ok(self) + res } - /// Performs a logical NOT on this expr. The access map is cleared. - pub fn not(mut self) -> Self { - self.access.clear(); - - self.expr = self.expr.not(); - self + pub(crate) fn matches_archetype(&self, mut f: F) -> bool + where + F: FnMut(ComponentIdx) -> bool, + { + self.cases + .iter() + .any(|case| case.iter().all(|(idx, _)| f(*idx))) } +} - /// XORs two access exprs together. - pub fn xor(mut self, other: &Self) -> Self { - self.access.union_assign(&other.access); - self.expr = self.expr.xor(&other.expr); - self +/// Equivalent to [`Self::new_false`]. +impl Default for ComponentAccess { + fn default() -> Self { + Self::new_false() } } @@ -294,59 +297,49 @@ impl ComponentAccessExpr { mod tests { use super::*; - type Cae = ComponentAccessExpr; + type Ca = ComponentAccess; + const A: ComponentIdx = ComponentIdx(0); const B: ComponentIdx = ComponentIdx(1); const C: ComponentIdx = ComponentIdx(2); - #[test] - fn t0() { - let expr = Cae::with(A, Access::ReadWrite); - let expr2 = expr.clone(); - let expr3 = Cae::with(A, Access::Read); - - assert!(!expr.is_compatible(&expr2)); - assert!(!expr.is_compatible(&expr3)); + #[track_caller] + fn check(ca: Ca, it: impl IntoIterator) { + assert_eq!( + ca.collect_conflicts(), + IndexSet::::from_iter(it) + ) } - #[test] - fn t1() { - let expr = Cae::with(A, Access::Read) - .and(&Cae::with(B, Access::ReadWrite)) - .unwrap(); - - let expr2 = Cae::with(A, Access::Read) - .and(&Cae::with(B, Access::None)) - .unwrap() - .and(&Cae::with(C, Access::ReadWrite)) - .unwrap(); - - assert!(expr.is_compatible(&expr2)); + fn a(access: Access) -> Ca { + Ca::var(A, access) } - #[test] - fn t2() { - let expr = Cae::with(A, Access::ReadWrite) - .and(&Cae::with(B, Access::None)) - .unwrap(); - - let expr2 = Cae::with(A, Access::ReadWrite) - .and(&Cae::without(B)) - .unwrap(); + fn b(access: Access) -> Ca { + Ca::var(B, access) + } - assert!(expr.is_compatible(&expr2)); + fn c(access: Access) -> Ca { + Ca::var(C, access) } #[test] - fn t3() { - let expr = Cae::with(A, Access::ReadWrite) - .and(&Cae::with(B, Access::None)) - .unwrap() - .and(&Cae::without(C)) - .unwrap(); - - let expr2 = Cae::with(A, Access::None); - - assert!(expr.is_compatible(&expr2)); + fn collect_conflicts() { + use Access::*; + + check(a(Read).and(&a(Read)), []); + check(a(ReadWrite).and(&a(ReadWrite)), [A]); + check(a(Read).and(&a(ReadWrite)).and(&a(None)), [A]); + + check( + a(ReadWrite).and(&b(None)).and(&a(Read).and(&b(None).not())), + [], + ); + + // (Xor<(&A, &B), (&B, &C)>, &mut B) + let left = a(Read).and(&b(Read)); + let right = b(Read).and(&c(Read)); + let xor = left.and(&right.not()).or(&right.and(&left.not())); + check(xor.and(&b(ReadWrite)), [B]); } } diff --git a/src/archetype.rs b/src/archetype.rs index 77de6ee..46a1d72 100644 --- a/src/archetype.rs +++ b/src/archetype.rs @@ -18,7 +18,7 @@ use crate::component::{ComponentIdx, ComponentInfo, Components}; use crate::entity::{Entities, EntityId, EntityLocation}; use crate::event::{EventIdx, EventPtr, TargetedEventIdx}; use crate::handler::{ - Config, HandlerInfo, HandlerInfoPtr, HandlerList, HandlerParam, Handlers, InitError, + HandlerConfig, HandlerInfo, HandlerInfoPtr, HandlerList, HandlerParam, Handlers, InitError, }; use crate::map::{Entry, HashMap}; use crate::prelude::World; @@ -529,7 +529,7 @@ unsafe impl HandlerParam for &'_ Archetypes { type Item<'a> = &'a Archetypes; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } @@ -676,9 +676,8 @@ impl Archetype { fn register_handler(&mut self, info: &mut HandlerInfo) { if info - .component_access() - .expr - .eval(|idx| self.column_of(idx).is_some()) + .archetype_filter() + .matches_archetype(|idx| self.column_of(idx).is_some()) { if self.entity_count() > 0 { info.handler_mut().refresh_archetype(self); @@ -687,10 +686,11 @@ impl Archetype { self.refresh_listeners.insert(info.ptr()); } - if let (Some(expr), EventIdx::Targeted(targeted_event_idx)) = - (info.targeted_event_expr(), info.received_event().index()) - { - if expr.eval(|idx| self.column_of(idx).is_some()) { + if let (Some(expr), EventIdx::Targeted(targeted_event_idx)) = ( + info.targeted_event_component_access(), + info.received_event().index(), + ) { + if expr.matches_archetype(|idx| self.column_of(idx).is_some()) { if let Some(list) = self.event_listeners.get_mut(targeted_event_idx) { list.insert(info.ptr(), info.priority()); } else { diff --git a/src/bit_set.rs b/src/bit_set.rs index e4f4bb6..79e568b 100644 --- a/src/bit_set.rs +++ b/src/bit_set.rs @@ -5,14 +5,14 @@ use alloc::{vec, vec::Vec}; use core::cmp::Ordering; use core::iter::FusedIterator; use core::marker::PhantomData; -use core::ops::{BitOr, BitOrAssign}; +use core::ops::{BitOr, BitOrAssign, BitXor, BitXorAssign}; use core::{any, fmt}; use crate::assert::GetDebugChecked; use crate::sparse::SparseIndex; /// A set data structure backed by a vector of bits. -pub struct BitSet { +pub(crate) struct BitSet { blocks: Vec, _marker: PhantomData, } @@ -30,9 +30,10 @@ type Block = usize; /// Number of bits in a block. const BITS: usize = Block::BITS as usize; +#[allow(dead_code)] impl BitSet { /// Create a new, empty bit set. - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self { blocks: vec![], _marker: PhantomData, @@ -40,7 +41,7 @@ impl BitSet { } /// Clears the set, removing all elements. - pub fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.blocks.clear(); } @@ -64,7 +65,7 @@ impl BitSet { /// Returns `true` if `self` has no elements in common with `other`. #[must_use] - pub fn is_disjoint(&self, other: &Self) -> bool { + pub(crate) fn is_disjoint(&self, other: &Self) -> bool { self.blocks .iter() .zip(other.blocks.iter()) @@ -73,7 +74,7 @@ impl BitSet { /// Returns the number of elements in the set. #[must_use] - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.blocks .iter() .map(|block| block.count_ones() as usize) @@ -82,16 +83,17 @@ impl BitSet { /// Returns `true` if the set contains no elements. #[must_use] - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.blocks.iter().all(|&block| block == 0) } } +#[allow(dead_code)] impl BitSet { /// Adds a value to the set. Returns whether the value was newly inserted. #[track_caller] #[inline] - pub fn insert(&mut self, value: T) -> bool { + pub(crate) fn insert(&mut self, value: T) -> bool { let idx = value.index(); let (block, bit) = div_rem(idx, BITS); @@ -108,7 +110,7 @@ impl BitSet { /// Removes a value from the set. Returns whether such an element was /// present. #[inline] - pub fn remove(&mut self, value: T) -> bool { + pub(crate) fn remove(&mut self, value: T) -> bool { let idx = value.index(); let (block, bit) = div_rem(idx, BITS); @@ -126,7 +128,7 @@ impl BitSet { /// Returns `true` if the set contains an element equal to the value. #[must_use] - pub fn contains(&self, value: T) -> bool { + pub(crate) fn contains(&self, value: T) -> bool { let idx = value.index(); let (block, bit) = div_rem(idx, BITS); @@ -137,7 +139,7 @@ impl BitSet { } /// Returns an iterator over the element in the set in ascending order. - pub fn iter(&self) -> Iter { + pub(crate) fn iter(&self) -> Iter { Iter { bits: self.blocks.first().copied().unwrap_or(0), block_idx: 0, @@ -147,7 +149,7 @@ impl BitSet { } /// Shrinks the capacity of the set as much as possible. - pub fn shrink_to_fit(&mut self) { + pub(crate) fn shrink_to_fit(&mut self) { while let Some(&last) = self.blocks.last() { if last != 0 { // There are bits in this block. @@ -162,12 +164,12 @@ impl BitSet { } impl BitOrAssign<&Self> for BitSet { - fn bitor_assign(&mut self, other: &Self) { - if self.blocks.len() < other.blocks.len() { - self.blocks.resize(other.blocks.len(), 0); + fn bitor_assign(&mut self, rhs: &Self) { + if self.blocks.len() < rhs.blocks.len() { + self.blocks.resize(rhs.blocks.len(), 0); } - for (a, b) in self.blocks.iter_mut().zip(other.blocks.iter()) { + for (a, b) in self.blocks.iter_mut().zip(rhs.blocks.iter()) { *a |= *b; } } @@ -176,8 +178,29 @@ impl BitOrAssign<&Self> for BitSet { impl BitOr<&Self> for BitSet { type Output = Self; - fn bitor(mut self, other: &Self) -> Self::Output { - self |= other; + fn bitor(mut self, rhs: &Self) -> Self::Output { + self |= rhs; + self + } +} + +impl BitXorAssign<&Self> for BitSet { + fn bitxor_assign(&mut self, rhs: &Self) { + if self.blocks.len() < rhs.blocks.len() { + self.blocks.resize(rhs.blocks.len(), 0); + } + + for (a, b) in self.blocks.iter_mut().zip(rhs.blocks.iter()) { + *a ^= *b; + } + } +} + +impl BitXor<&Self> for BitSet { + type Output = Self; + + fn bitxor(mut self, rhs: &Self) -> Self::Output { + self ^= rhs; self } } @@ -230,13 +253,24 @@ where /// An iterator over the items in a [`BitSet`]. #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Iter<'a, T = usize> { +pub(crate) struct Iter<'a, T = usize> { bits: Block, block_idx: usize, blocks: &'a [Block], _marker: PhantomData T>, } +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Self { + bits: self.bits, + block_idx: self.block_idx, + blocks: self.blocks, + _marker: PhantomData, + } + } +} + impl<'a, T: SparseIndex> Iterator for Iter<'a, T> { type Item = T; diff --git a/src/bool_expr.rs b/src/bool_expr.rs deleted file mode 100644 index 82317bb..0000000 --- a/src/bool_expr.rs +++ /dev/null @@ -1,302 +0,0 @@ -//! Boolean expressions. - -#[cfg(not(feature = "std"))] -use alloc::{vec, vec::Vec}; -use core::{fmt, mem}; - -use crate::bit_set::BitSet; -use crate::sparse::SparseIndex; - -/// Represents an arbitrary boolean expression from boolean algebra. Values of -/// type `T` are used as the variables. -pub struct BoolExpr { - // The boolean expression in disjunctive normal form, - // e.g. (A ∧ B ∧ ¬C) ∨ (D ∧ ¬E ∧ ¬F). This is an "OR of ANDs". - ands: Vec>, -} - -struct Ands { - vars: BitSet, - negated_vars: BitSet, -} - -impl Ands { - fn new() -> Self { - Self { - vars: BitSet::new(), - negated_vars: BitSet::new(), - } - } -} - -#[allow(clippy::should_implement_trait)] -impl BoolExpr { - /// Creates an new expression of either `true` or `false`. - pub fn new(b: bool) -> Self { - Self { - ands: if b { vec![Ands::new()] } else { vec![] }, - } - } - - /// Create an expression from a single variable. - pub fn var(value: T) -> Self - where - T: SparseIndex, - { - Self { - ands: vec![Ands { - vars: { - let mut vars = BitSet::new(); - vars.insert(value); - vars - }, - negated_vars: BitSet::new(), - }], - } - } - - /// Create an expression from a single negated variable. Equivalent to - /// `BoolExpr::var(value).not()`. - pub fn not_var(value: T) -> Self - where - T: SparseIndex, - { - Self { - ands: vec![Ands { - vars: BitSet::new(), - negated_vars: { - let mut negated_vars = BitSet::new(); - negated_vars.insert(value); - negated_vars - }, - }], - } - } - - /// Evaluate the boolean expression. `get_var` provides the values of the - /// variables in the expression. - /// - /// # Examples - /// - /// ```rust - /// use evenio::bool_expr::BoolExpr; - /// - /// const A: u32 = 0; - /// const B: u32 = 1; - /// - /// let expr = BoolExpr::var(A).xor(&BoolExpr::var(B)); - /// - /// let get_var = |a, b| { - /// move |var| match var { - /// A => a, - /// B => b, - /// _ => false, - /// } - /// }; - /// - /// assert_eq!(expr.eval(get_var(false, false)), false); - /// assert_eq!(expr.eval(get_var(true, false)), true); - /// assert_eq!(expr.eval(get_var(false, true)), true); - /// assert_eq!(expr.eval(get_var(true, true)), false); - /// ``` - pub fn eval(&self, mut get_var: F) -> bool - where - T: SparseIndex, - F: FnMut(T) -> bool, - { - 'ands: for ands in &self.ands { - for var in &ands.vars { - if !get_var(var) { - continue 'ands; - } - } - - for var in &ands.negated_vars { - if get_var(var) { - continue 'ands; - } - } - - return true; - } - - false - } - - /// AND two expressions together. - #[must_use] - pub fn and(mut self, other: &Self) -> Self - where - T: SparseIndex, - { - let mut res = Vec::new(); - for this in &self.ands { - for other in &other.ands { - let mut new_ands = this.clone(); - - new_ands.vars |= &other.vars; - new_ands.negated_vars |= &other.negated_vars; - - // Skip contradictions. - if new_ands.vars.is_disjoint(&new_ands.negated_vars) { - res.push(new_ands); - } - } - } - - self.ands = res; - self - } - - /// OR two expressions together. - #[must_use] - pub fn or(mut self, other: &Self) -> Self - where - T: SparseIndex, - { - self.ands.extend(other.ands.iter().cloned()); - self - } - - /// Negates `self`. - #[must_use] - pub fn not(mut self) -> Self - where - T: SparseIndex, - { - let mut res = Self::new(true); - - // Apply De Morgan's laws. - for mut ands in mem::take(&mut self.ands) { - let mut ors = Self::new(false); - - mem::swap(&mut ands.vars, &mut ands.negated_vars); - - for var in &ands.vars { - let mut a = Ands::new(); - a.vars.insert(var); - ors.ands.push(a); - } - - for negated_var in &ands.negated_vars { - let mut a = Ands::new(); - a.negated_vars.insert(negated_var); - ors.ands.push(a); - } - - res = res.and(&ors); - } - - res - } - - /// XOR two expressions together. - pub fn xor(self, other: &Self) -> Self - where - T: SparseIndex, - { - // A ⊻ B ≡ (A ∧ ¬B) ∨ (B ∧ ¬A) - self.clone() - .and(&other.clone().not()) - .or(&other.clone().and(&self.not())) - } - - /// Determines if `self` and `other` are disjoint, i.e. if there is no - /// combination of values the variables could have to make both expressions - /// true at the same time. - /// - /// # Examples - /// - /// ```rust - /// use evenio::bool_expr::BoolExpr; - /// - /// // `A` is not disjoint with `B` - /// assert!(!BoolExpr::var(A).is_disjoint(&BoolExpr::var(B))); - /// - /// // `A` is disjoint with `¬A`. - /// assert!(BoolExpr::var(A).is_disjoint(&BoolExpr::not_var(A))); - /// - /// // `A ∧ ¬A` is disjoint with `B ∧ C`. - /// let left = BoolExpr::var(A).and(&BoolExpr::not_var(A)); - /// let right = BoolExpr::var(C).and(&BoolExpr::var(D)); - /// assert!(left.is_disjoint(&right)); - /// - /// const A: u32 = 0; - /// const B: u32 = 1; - /// const C: u32 = 2; - /// const D: u32 = 3; - /// ``` - pub fn is_disjoint(&self, other: &Self) -> bool { - self.ands.iter().all(|this| { - other.ands.iter().all(|other| { - !this.vars.is_disjoint(&this.negated_vars) - || !other.vars.is_disjoint(&other.negated_vars) - || !this.vars.is_disjoint(&other.negated_vars) - || !other.vars.is_disjoint(&this.negated_vars) - }) - }) - } -} - -impl Clone for BoolExpr { - fn clone(&self) -> Self { - Self { - ands: self.ands.clone(), - } - } -} - -impl Clone for Ands { - fn clone(&self) -> Self { - Self { - vars: self.vars.clone(), - negated_vars: self.negated_vars.clone(), - } - } -} - -impl fmt::Debug for BoolExpr -where - T: SparseIndex + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.ands.is_empty() { - write!(f, "⊥")?; - } else { - let mut first = true; - - for ands in &self.ands { - if !first { - write!(f, " ∨ ")?; - } - first = false; - - if ands.vars.is_empty() && ands.negated_vars.is_empty() { - write!(f, "⊤")?; - } else { - let mut first = true; - - for var in &ands.vars { - if !first { - write!(f, " ∧ ")?; - } - first = false; - - write!(f, "{var:?}")?; - } - - for var in &ands.negated_vars { - if !first { - write!(f, " ∧ ")?; - } - first = false; - - write!(f, "¬{var:?}")?; - } - } - } - } - - Ok(()) - } -} diff --git a/src/component.rs b/src/component.rs index 79c24d6..fc03948 100644 --- a/src/component.rs +++ b/src/component.rs @@ -14,7 +14,7 @@ use crate::assert::UnwrapDebugChecked; use crate::drop::DropFn; use crate::entity::EntityLocation; use crate::event::{Event, EventId, EventPtr}; -use crate::handler::{Config, HandlerInfo, HandlerParam, InitError}; +use crate::handler::{HandlerConfig, HandlerInfo, HandlerParam, InitError}; use crate::map::{Entry, IndexSet, TypeIdMap}; use crate::prelude::World; use crate::slot_map::{Key, SlotMap}; @@ -175,7 +175,7 @@ unsafe impl HandlerParam for &'_ Components { type Item<'a> = &'a Components; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } diff --git a/src/entity.rs b/src/entity.rs index 94c4ecf..f452799 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -4,7 +4,7 @@ use core::ops::Index; use crate::archetype::{ArchetypeIdx, ArchetypeRow}; use crate::event::EventPtr; -use crate::handler::{Config, HandlerInfo, HandlerParam, InitError}; +use crate::handler::{HandlerConfig, HandlerInfo, HandlerParam, InitError}; use crate::prelude::World; use crate::slot_map::{Key, NextKeyIter, SlotMap}; use crate::world::UnsafeWorldCell; @@ -110,7 +110,7 @@ unsafe impl HandlerParam for &'_ Entities { type Item<'a> = &'a Entities; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } diff --git a/src/event.rs b/src/event.rs index d306211..bdf9237 100644 --- a/src/event.rs +++ b/src/event.rs @@ -2,7 +2,7 @@ use alloc::borrow::Cow; #[cfg(not(feature = "std"))] -use alloc::{format, vec, vec::Vec}; +use alloc::{vec, vec::Vec}; use core::alloc::Layout; use core::any::TypeId; use core::marker::PhantomData; @@ -26,7 +26,7 @@ use crate::component::ComponentIdx; use crate::drop::DropFn; use crate::entity::{EntityId, EntityLocation}; use crate::fetch::FetcherState; -use crate::handler::{Config, HandlerInfo, HandlerParam, InitError}; +use crate::handler::{HandlerConfig, HandlerInfo, HandlerParam, InitError}; use crate::map::{Entry, TypeIdMap}; use crate::prelude::Component; use crate::query::Query; @@ -214,7 +214,7 @@ unsafe impl HandlerParam for &'_ Events { type Item<'a> = &'a Events; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } @@ -859,10 +859,13 @@ unsafe impl HandlerParam for Receiver<'_, E> { type Item<'a> = Receiver<'a, E>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { let () = AssertUntargetedEvent::::ASSERTION; - set_received_event::(world, config, Access::Read)?; + let event_id = world.add_event::(); + + config.set_received_event(event_id); + config.set_received_event_access(Access::Read); Ok(()) } @@ -893,31 +896,19 @@ unsafe impl HandlerParam for Receiver<'_, E, Q> { type Item<'a> = Receiver<'a, E, Q>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { let () = AssertTargetedEvent::::ASSERTION; - set_received_event::(world, config, Access::Read)?; - - let (expr, state) = Q::init(world, config)?; - - let res = FetcherState::new(state); + let event_id = world.add_event::(); - config.targeted_event_expr = expr.expr.clone(); + let (ca, state) = Q::init(world, config)?; - if let Ok(new_component_access) = expr.or(&config.component_access) { - config.component_access = new_component_access; - } else { - return Err(InitError( - format!( - "query `{}` has incompatible component access with previous queries in this \ - handler", - any::type_name::() - ) - .into(), - )); - } + config.set_received_event(event_id); + config.set_received_event_access(Access::Read); + config.set_targeted_event_component_access(ca.clone()); + config.push_component_access(ca); - Ok(res) + Ok(FetcherState::new(state)) } unsafe fn get<'a>( @@ -978,11 +969,14 @@ unsafe impl HandlerParam for ReceiverMut<'_, E> { type Item<'a> = ReceiverMut<'a, E>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { let () = AssertMutable::::EVENT; let () = AssertUntargetedEvent::::ASSERTION; - set_received_event::(world, config, Access::ReadWrite)?; + let event_id = world.add_event::(); + + config.set_received_event(event_id); + config.set_received_event_access(Access::ReadWrite); Ok(()) } @@ -1010,32 +1004,20 @@ unsafe impl HandlerParam for ReceiverMut<'_, E, Q> type Item<'a> = ReceiverMut<'a, E, Q>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { let () = AssertMutable::::EVENT; let () = AssertTargetedEvent::::ASSERTION; - set_received_event::(world, config, Access::ReadWrite)?; - - let (expr, state) = Q::init(world, config)?; - - let res = FetcherState::new(state); + let event_id = world.add_event::(); - config.targeted_event_expr = expr.expr.clone(); + let (ca, state) = Q::init(world, config)?; - if let Ok(new_component_access) = expr.or(&config.component_access) { - config.component_access = new_component_access; - } else { - return Err(InitError( - format!( - "query `{}` has incompatible component access with previous queries in this \ - handler", - any::type_name::() - ) - .into(), - )); - } + config.set_received_event(event_id); + config.set_received_event_access(Access::ReadWrite); + config.set_targeted_event_component_access(ca.clone()); + config.push_component_access(ca); - Ok(res) + Ok(FetcherState::new(state)) } unsafe fn get<'a>( @@ -1078,48 +1060,6 @@ where } } -fn set_received_event( - world: &mut World, - config: &mut Config, - access: Access, -) -> Result { - let id = world.add_event::(); - - if let Some(received_event) = config.received_event { - if received_event != id { - let other = world - .events() - .get(received_event) - .map_or("", |info| info.name()); - - return Err(InitError( - format!( - "tried to set `{}` as the received event for this handler, but the handler \ - was already configured to receive `{other}`. Handlers must listen for \ - exactly one event type", - any::type_name::(), - ) - .into(), - )); - } - } - - config.received_event = Some(id); - - if !config.received_event_access.set_if_compatible(access) { - return Err(InitError( - format!( - "tried to set `{access:?}` as the received event access for this handler, but it \ - was already set to `{:?}`", - config.received_event_access - ) - .into(), - )); - } - - Ok(id) -} - /// Indicates the absence of a [`ReceiverQuery`]. #[derive(Clone, Copy, Debug)] pub struct NullReceiverQuery; @@ -1276,20 +1216,8 @@ unsafe impl HandlerParam for Sender<'_, T> { type Item<'a> = Sender<'a, T>; - fn init(world: &mut World, config: &mut Config) -> Result { - if !config - .event_queue_access - .set_if_compatible(Access::ReadWrite) - { - return Err(InitError( - format!( - "`{}` has conflicting access with a previous handler parameter. Only one \ - handler parameter can send events", - any::type_name::() - ) - .into(), - )); - } + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { + config.set_event_queue_access(Access::ReadWrite); let state = T::new_state(world); diff --git a/src/fetch.rs b/src/fetch.rs index bd1efd0..abe25d6 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -1,6 +1,5 @@ //! Accessing components on entities. -use alloc::format; use core::iter::FusedIterator; use core::ptr::NonNull; use core::{any, fmt}; @@ -9,7 +8,7 @@ use crate::archetype::{Archetype, ArchetypeIdx, ArchetypeRow, Archetypes}; use crate::assert::{assume_debug_checked, UnwrapDebugChecked}; use crate::entity::{Entities, EntityId, EntityLocation}; use crate::event::EventPtr; -use crate::handler::{Config, HandlerInfo, HandlerParam, InitError}; +use crate::handler::{HandlerConfig, HandlerInfo, HandlerParam, InitError}; use crate::query::{Query, ReadOnlyQuery}; use crate::sparse_map::SparseMap; use crate::world::{UnsafeWorldCell, World}; @@ -29,26 +28,12 @@ impl FetcherState { } } - pub(crate) fn init(world: &mut World, config: &mut Config) -> Result { - let (expr, state) = Q::init(world, config)?; + pub(crate) fn init(world: &mut World, config: &mut HandlerConfig) -> Result { + let (ca, state) = Q::init(world, config)?; - let res = FetcherState::new(state); + config.push_component_access(ca); - match expr.or(&config.component_access) { - Ok(new_component_access) => config.component_access = new_component_access, - Err(_) => { - return Err(InitError( - format!( - "query `{}` has incompatible component access with previous queries in \ - this handler", - any::type_name::() - ) - .into(), - )) - } - } - - Ok(res) + Ok(FetcherState::new(state)) } #[inline] @@ -320,7 +305,7 @@ where type Item<'a> = Fetcher<'a, Q>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { FetcherState::init(world, config) } @@ -379,7 +364,7 @@ unsafe impl HandlerParam for Single<'_, Q> { type Item<'a> = Single<'a, Q>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { FetcherState::init(world, config) } @@ -423,7 +408,7 @@ unsafe impl HandlerParam for TrySingle<'_, Q> { type Item<'a> = TrySingle<'a, Q>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { FetcherState::init(world, config) } diff --git a/src/handler.rs b/src/handler.rs index 23f9e0b..81348cf 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -16,12 +16,11 @@ use core::{any, fmt}; use evenio_macros::all_tuples; pub use evenio_macros::HandlerParam; -use crate::access::{Access, ComponentAccessExpr}; +use crate::access::{Access, ComponentAccess}; use crate::aliased_box::AliasedBox; use crate::archetype::Archetype; use crate::assert::UnwrapDebugChecked; use crate::bit_set::BitSet; -use crate::bool_expr::BoolExpr; use crate::component::ComponentIdx; use crate::entity::EntityLocation; use crate::event::{Event, EventId, EventIdx, EventPtr, TargetedEventIdx, UntargetedEventIdx}; @@ -229,7 +228,7 @@ unsafe impl HandlerParam for &'_ Handlers { type Item<'a> = &'a Handlers; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } @@ -313,13 +312,14 @@ pub(crate) struct HandlerInfoInner { pub(crate) order: u64, pub(crate) received_event: EventId, pub(crate) received_event_access: Access, - pub(crate) targeted_event_expr: BoolExpr, + pub(crate) targeted_event_component_access: ComponentAccess, pub(crate) sent_untargeted_events: BitSet, pub(crate) sent_targeted_events: BitSet, pub(crate) event_queue_access: Access, - pub(crate) component_access: ComponentAccessExpr, + pub(crate) component_access: ComponentAccess, + pub(crate) archetype_filter: ComponentAccess, pub(crate) referenced_components: BitSet, - pub(crate) priority: Priority, + pub(crate) priority: HandlerPriority, // SAFETY: There is intentionally no public accessor for this field as it would lead to mutable // aliasing. pub(crate) handler: H, @@ -370,19 +370,31 @@ impl HandlerInfo { /// Gets the expression describing the handler's targeted event query, or /// `None` if this handler is not targeted. - pub fn targeted_event_expr(&self) -> Option<&BoolExpr> { + pub fn targeted_event_component_access(&self) -> Option<&ComponentAccess> { self.received_event() .is_targeted() - .then(|| unsafe { &(*AliasedBox::as_ptr(&self.0)).targeted_event_expr }) + .then(|| unsafe { &(*AliasedBox::as_ptr(&self.0)).targeted_event_component_access }) } /// Returns the set of untargeted events this handler sends. - pub fn sent_untargeted_events(&self) -> &BitSet { + pub fn sent_untargeted_events( + &self, + ) -> impl Iterator + Clone + fmt::Debug + '_ { + self.sent_untargeted_events_bitset().iter() + } + + pub(crate) fn sent_untargeted_events_bitset(&self) -> &BitSet { unsafe { &(*AliasedBox::as_ptr(&self.0)).sent_untargeted_events } } /// Returns the set of targeted events this handler sends. - pub fn sent_targeted_events(&self) -> &BitSet { + pub fn sent_targeted_events( + &self, + ) -> impl Iterator + Clone + fmt::Debug + '_ { + self.sent_targeted_events_bitset().iter() + } + + pub(crate) fn sent_targeted_events_bitset(&self) -> &BitSet { unsafe { &(*AliasedBox::as_ptr(&self.0)).sent_targeted_events } } @@ -392,20 +404,32 @@ impl HandlerInfo { } /// Gets the expression describing this handler's access - pub fn component_access(&self) -> &ComponentAccessExpr { + pub fn component_access(&self) -> &ComponentAccess { unsafe { &(*AliasedBox::as_ptr(&self.0)).component_access } } + /// Returns the [`ComponentAccess`] used for matching archetypes. + pub fn archetype_filter(&self) -> &ComponentAccess { + unsafe { &(*AliasedBox::as_ptr(&self.0)).archetype_filter } + } + /// Gets the set of components referenced by this handler. /// /// Referenced components are components used by the handler in any way. /// Used for cleanup when removing components. - pub fn referenced_components(&self) -> &BitSet { - unsafe { &(*AliasedBox::as_ptr(&self.0)).referenced_components } + pub fn referenced_components( + &self, + ) -> impl Iterator + Clone + fmt::Debug + '_ { + unsafe { &(*AliasedBox::as_ptr(&self.0)).referenced_components }.iter() } - /// Gets the [`Priority`] of this handler. - pub fn priority(&self) -> Priority { + /// Does this handler reference the given component? + pub fn references_component(&self, idx: ComponentIdx) -> bool { + unsafe { &(*AliasedBox::as_ptr(&self.0)).referenced_components }.contains(idx) + } + + /// Gets the [`HandlerPriority`] of this handler. + pub fn priority(&self) -> HandlerPriority { unsafe { (*AliasedBox::as_ptr(&self.0)).priority } } @@ -427,11 +451,12 @@ impl fmt::Debug for HandlerInfo { .field("order", &self.order()) .field("received_event", &self.received_event()) .field("received_event_access", &self.received_event_access()) - .field("targeted_event_expr", &self.targeted_event_expr()) + .field("targeted_event_component_access", &self.targeted_event_component_access()) .field("sent_untargeted_events", &self.sent_untargeted_events()) .field("sent_targeted_events", &self.sent_targeted_events()) .field("event_queue_access", &self.event_queue_access()) .field("component_access", &self.component_access()) + .field("archetype_filter", &self.archetype_filter()) .field("referenced_components", &self.referenced_components()) .field("priority", &self.priority()) // Don't access the `handler` field. @@ -457,20 +482,20 @@ impl HandlerList { } } - pub(crate) fn insert(&mut self, ptr: HandlerInfoPtr, priority: Priority) { + pub(crate) fn insert(&mut self, ptr: HandlerInfoPtr, priority: HandlerPriority) { assert!(self.entries.len() < u32::MAX as usize); match priority { - Priority::High => { + HandlerPriority::High => { self.entries.insert(self.before as usize, ptr); self.before += 1; self.after += 1; } - Priority::Medium => { + HandlerPriority::Medium => { self.entries.insert(self.after as usize, ptr); self.after += 1; } - Priority::Low => { + HandlerPriority::Low => { self.entries.push(ptr); } } @@ -585,15 +610,15 @@ pub trait IntoHandler: Sized { } /// Returns a wrapper which sets the priority of this handler to - /// [`Priority::High`]. - fn high(self) -> High { - High(self.into_handler()) + /// [`HandlerPriority::High`]. + fn high(self) -> HighPriority { + HighPriority(self.into_handler()) } /// Returns a wrapper which sets the priority of this handler to - /// [`Priority::Low`]. - fn low(self) -> Low { - Low(self.into_handler()) + /// [`HandlerPriority::Low`]. + fn low(self) -> LowPriority { + LowPriority(self.into_handler()) } } @@ -634,7 +659,7 @@ impl Handler for NoTypeId { self.0.name() } - fn init(&mut self, world: &mut World, config: &mut Config) -> Result<(), InitError> { + fn init(&mut self, world: &mut World, config: &mut HandlerConfig) -> Result<(), InitError> { self.0.init(world, config) } @@ -659,9 +684,9 @@ impl Handler for NoTypeId { /// The wrapper handler returned by [`IntoHandler::high`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] -pub struct High(pub S); +pub struct HighPriority(pub S); -impl Handler for High { +impl Handler for HighPriority { fn type_id(&self) -> Option { self.0.type_id() } @@ -670,9 +695,9 @@ impl Handler for High { self.0.name() } - fn init(&mut self, world: &mut World, config: &mut Config) -> Result<(), InitError> { + fn init(&mut self, world: &mut World, config: &mut HandlerConfig) -> Result<(), InitError> { let res = self.0.init(world, config); - config.priority = Priority::High; + config.set_priority(HandlerPriority::High); res } @@ -697,9 +722,9 @@ impl Handler for High { /// The wrapper handler returned by [`IntoHandler::low`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] -pub struct Low(pub S); +pub struct LowPriority(pub S); -impl Handler for Low { +impl Handler for LowPriority { fn type_id(&self) -> Option { self.0.type_id() } @@ -708,9 +733,9 @@ impl Handler for Low { self.0.name() } - fn init(&mut self, world: &mut World, config: &mut Config) -> Result<(), InitError> { + fn init(&mut self, world: &mut World, config: &mut HandlerConfig) -> Result<(), InitError> { let res = self.0.init(world, config); - config.priority = Priority::Low; + config.set_priority(HandlerPriority::Low); res } @@ -750,7 +775,7 @@ pub trait Handler: Send + Sync + 'static { fn name(&self) -> Cow<'static, str>; /// Initializes the handler. Returns [`InitError`] on failure. - fn init(&mut self, world: &mut World, config: &mut Config) -> Result<(), InitError>; + fn init(&mut self, world: &mut World, config: &mut HandlerConfig) -> Result<(), InitError>; /// Execute the handler by passing in the handler's metadata, a pointer to /// the received event of the configured type, the entity location of @@ -763,12 +788,14 @@ pub trait Handler: Send + Sync + 'static { /// - `info` must be the correct information for this handler. /// - `event_ptr` must point to the correct type of event configured by this /// handler in [`init`]. - /// - `target_location` must be a valid location to an entity matching - /// [`Config::targeted_event_expr`], unless the event is not targeted. + /// - `target_location` must be a valid location to an entity matching the + /// component access set by [`set_targeted_event_component_access`], + /// unless the event is not targeted. /// - `world` must have permission to access all data configured by this /// handler in [`init`]. /// /// [`init`]: Self::init + /// [`set_targeted_event_component_access`]: HandlerConfig::set_targeted_event_component_access unsafe fn run( &mut self, info: &HandlerInfo, @@ -819,13 +846,117 @@ impl fmt::Display for InitError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for InitError {} +/// The configuration of a handler. Accessible during handler initialization. +#[derive(Clone, Default, Debug)] +pub struct HandlerConfig { + pub(crate) priority: HandlerPriority, + pub(crate) received_event: ReceivedEventId, + pub(crate) received_event_access: MaybeInvalidAccess, + pub(crate) targeted_event_component_access: ComponentAccess, + pub(crate) sent_untargeted_events: BitSet, + pub(crate) sent_targeted_events: BitSet, + pub(crate) event_queue_access: MaybeInvalidAccess, + pub(crate) component_accesses: Vec, + pub(crate) referenced_components: BitSet, +} + +impl HandlerConfig { + /// Creates the default configuration. + pub fn new() -> Self { + Self::default() + } + + /// Overwrites the [`HandlerPriority`] of this handler. + /// + /// Handlers default to [`HandlerPriority::Medium`]. + pub fn set_priority(&mut self, priority: HandlerPriority) { + self.priority = priority; + } + + /// Sets the event sent by this handler. Causes an initialization error + /// if a different event was previously set. + pub fn set_received_event(&mut self, event: EventId) { + self.received_event = match self.received_event { + ReceivedEventId::None => ReceivedEventId::Ok(event), + ReceivedEventId::Ok(old_event) => { + if old_event == event { + ReceivedEventId::Ok(event) + } else { + ReceivedEventId::Invalid + } + } + ReceivedEventId::Invalid => ReceivedEventId::Invalid, + }; + } + + /// Sets the [`Access`] + pub fn set_received_event_access(&mut self, access: Access) { + self.received_event_access = match self.received_event_access { + MaybeInvalidAccess::Ok(old_access) => access + .join(old_access) + .map_or(MaybeInvalidAccess::Invalid, MaybeInvalidAccess::Ok), + MaybeInvalidAccess::Invalid => MaybeInvalidAccess::Invalid, + }; + } + + /// Sets the [`ComponentAccess`] describing the data accessed on the target + /// of the event received by this handler. This should be a subset of the + /// component access given to [`Self::push_component_access`]. + /// + /// Has no effect if the received event is untargeted. Defaults to + /// [`ComponentAccess::new_false`]. + pub fn set_targeted_event_component_access(&mut self, component_access: ComponentAccess) { + self.targeted_event_component_access = component_access; + } + + /// Inserts an event type to the set of event types this handler is able to + /// send. + /// + /// Returns whether the given event was already configured to be sent. + pub fn insert_sent_event(&mut self, event: EventId) -> bool { + match event.index() { + EventIdx::Targeted(e) => self.sent_targeted_events.insert(e), + EventIdx::Untargeted(e) => self.sent_untargeted_events.insert(e), + } + } + + /// Sets the handler's access to the event queue. Produces a configuration + /// failure if the given access conflicts with the previously set access. + pub fn set_event_queue_access(&mut self, access: Access) { + self.event_queue_access = match self.event_queue_access { + MaybeInvalidAccess::Ok(old_access) => access + .join(old_access) + .map_or(MaybeInvalidAccess::Invalid, MaybeInvalidAccess::Ok), + MaybeInvalidAccess::Invalid => MaybeInvalidAccess::Invalid, + }; + } + + /// Pushes a [`ComponentAccess`] to the list of [`ComponentAccess`] for this + /// handler. The conjunction of the accesses determines the component access + /// of the whole handler, while the disjunction determines which archetypes + /// are matched. + /// + /// Generally, this means that there should be one [`ComponentAccess`] + /// pushed per handler param that accesses components. + pub fn push_component_access(&mut self, component_access: ComponentAccess) { + self.component_accesses.push(component_access); + } + + /// Inserts a component into the set of components referenced by this + /// handler. The set is used for cleanup when a component type is removed + /// from the world. + pub fn insert_referenced_components(&mut self, comp: ComponentIdx) { + self.referenced_components.insert(comp); + } +} + /// The priority of a handler relative to other handlers that handle the same /// event. /// /// If multiple handlers have the same priority, then the order they were added /// to the [`World`] is used as a fallback. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)] -pub enum Priority { +pub enum HandlerPriority { /// The handler runs before other handlers. High, /// The default handler priority. @@ -835,60 +966,23 @@ pub enum Priority { Low, } -/// The Configuration of a handler. Accessible during handler initialization. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub struct Config { - /// The priority of this handler. - pub priority: Priority, - /// The event type to be received by the handler. - /// - /// Defaults to `None`, but must be assigned to `Some` before configuration - /// is finished. - pub received_event: Option, - /// Access to the received event value. - pub received_event_access: Access, - /// The targeted event filter. This should be a subset of - /// [`Self::component_access`]. - pub targeted_event_expr: BoolExpr, - /// The set of untargeted events sent by the handler. - pub sent_untargeted_events: BitSet, - /// The set of targeted events sent by the handler. - pub sent_targeted_events: BitSet, - /// Access to the queue of events. - pub event_queue_access: Access, - /// Expression describing the components accessed by the handler. - pub component_access: ComponentAccessExpr, - /// The set of components referenced by this handler. Used for handler - /// cleanup when a component is removed. - /// - /// This is a superset of the components accessed by this handler. Consider - /// the query `Has<&C>`: `Has` does not access `C`, but it still makes use - /// of `C`'s component index, so the whole handler must be removed when - /// component `C` is removed. - pub referenced_components: BitSet, +#[derive(Copy, Clone, Default, Debug)] +pub(crate) enum ReceivedEventId { + #[default] + None, + Ok(EventId), + Invalid, } -impl Config { - /// Creates the default configuration. - pub fn new() -> Self { - Self { - priority: Default::default(), - received_event: Default::default(), - received_event_access: Default::default(), - targeted_event_expr: BoolExpr::new(false), - sent_untargeted_events: Default::default(), - sent_targeted_events: Default::default(), - event_queue_access: Default::default(), - component_access: ComponentAccessExpr::new(false), - referenced_components: Default::default(), - } - } +#[derive(Copy, Clone, Debug)] +pub(crate) enum MaybeInvalidAccess { + Ok(Access), + Invalid, } -impl Default for Config { +impl Default for MaybeInvalidAccess { fn default() -> Self { - Self::new() + Self::Ok(Access::default()) } } @@ -936,11 +1030,11 @@ pub unsafe trait HandlerParam { /// of `Self` but with the lifetime of `'a`. type Item<'a>; - /// Initializes the handler using the input [`World`] and [`Config`]. + /// Initializes the handler using the input [`World`] and [`HandlerConfig`]. /// /// If initialization fails, [`InitError`] is returned and the handler is /// not considered initialized. - fn init(world: &mut World, config: &mut Config) -> Result; + fn init(world: &mut World, config: &mut HandlerConfig) -> Result; /// Obtains a new instance of the handler parameter. /// @@ -974,7 +1068,7 @@ unsafe impl HandlerParam for PhantomData { type Item<'a> = Self; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } @@ -1002,7 +1096,7 @@ macro_rules! impl_handler_param_tuple { type Item<'a> = ($($P::Item<'a>,)*); #[inline] - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { Ok(( $( $P::init(world, config)?, @@ -1095,7 +1189,7 @@ where Cow::Borrowed(any::type_name::()) } - fn init(&mut self, world: &mut World, config: &mut Config) -> Result<(), InitError> { + fn init(&mut self, world: &mut World, config: &mut HandlerConfig) -> Result<(), InitError> { self.state = Some(::init(world, config)?); Ok(()) } @@ -1221,7 +1315,7 @@ unsafe impl HandlerParam for Local<'_, T> { type Item<'a> = Local<'a, T>; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(Exclusive::new(T::default())) } @@ -1268,7 +1362,7 @@ unsafe impl HandlerParam for &'_ HandlerInfo { type Item<'a> = &'a HandlerInfo; - fn init(_world: &mut World, _config: &mut Config) -> Result { + fn init(_world: &mut World, _config: &mut HandlerConfig) -> Result { Ok(()) } @@ -1294,7 +1388,7 @@ unsafe impl HandlerParam for std::sync::Mutex

{ type Item<'a> = std::sync::Mutex>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { P::init(world, config) } @@ -1324,7 +1418,7 @@ unsafe impl HandlerParam for std::sync::RwLock

{ type Item<'a> = std::sync::RwLock>; - fn init(world: &mut World, config: &mut Config) -> Result { + fn init(world: &mut World, config: &mut HandlerConfig) -> Result { P::init(world, config) } @@ -1440,6 +1534,7 @@ mod tests { struct E; world.add_handler(|_: Receiver, info: &HandlerInfo| { + // For Miri. let _foo = info.name(); let _bar = info.received_event(); let _baz = info.referenced_components(); diff --git a/src/lib.rs b/src/lib.rs index f373c44..46f7db9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,9 +14,8 @@ pub mod access; mod aliased_box; pub mod archetype; mod assert; -pub mod bit_set; +mod bit_set; mod blob_vec; -pub mod bool_expr; pub mod component; pub mod drop; pub mod entity; diff --git a/src/query.rs b/src/query.rs index cfcaffd..2ae583d 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,19 +1,18 @@ //! Type-level DSL for retrieving data from entities. -use alloc::format; +use core::fmt; use core::marker::PhantomData; use core::ptr::NonNull; -use core::{any, fmt}; use evenio_macros::all_tuples; pub use evenio_macros::Query; -use crate::access::{Access, ComponentAccessExpr}; +use crate::access::{Access, ComponentAccess}; use crate::archetype::{Archetype, ArchetypeRow}; use crate::assert::{AssertMutable, UnwrapDebugChecked}; use crate::component::{Component, ComponentIdx}; use crate::entity::EntityId; -use crate::handler::{Config, InitError}; +use crate::handler::{HandlerConfig, InitError}; use crate::world::World; /// Types that can be fetched from an entity. @@ -72,8 +71,8 @@ pub unsafe trait Query { /// accessed by the query and a new instance of [`Self::State`]. fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError>; + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError>; /// Returns a new [`Self::State`] instance. fn new_state(world: &mut World) -> Self::State; @@ -86,7 +85,7 @@ pub unsafe trait Query { /// # Safety /// - `row` must be in bounds. /// - Must have the appropriate component access permissions described by - /// the [`ComponentAccessExpr`] returned by [`init`]. + /// the [`ComponentAccess`] returned by [`init`]. /// - The lifetime of the item is chosen by the caller. The item must not /// outlive the data it references. /// @@ -115,13 +114,13 @@ unsafe impl Query for &'_ C { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { let idx = Self::new_state(world); - let expr = ComponentAccessExpr::with(idx, Access::Read); + let ca = ComponentAccess::var(idx, Access::Read); config.referenced_components.insert(idx); - Ok((expr, idx)) + Ok((ca, idx)) } fn new_state(world: &mut World) -> Self::State { @@ -148,15 +147,15 @@ unsafe impl Query for &'_ mut C { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { let () = AssertMutable::::COMPONENT; let idx = Self::new_state(world); - let expr = ComponentAccessExpr::with(idx, Access::ReadWrite); + let ca = ComponentAccess::var(idx, Access::ReadWrite); config.referenced_components.insert(idx); - Ok((expr, idx)) + Ok((ca, idx)) } fn new_state(world: &mut World) -> Self::State { @@ -182,30 +181,19 @@ macro_rules! impl_query_tuple { type State = ($($Q::State,)*); + #[allow(unused_mut)] fn init( world: &mut World, - config: &mut Config - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - #![allow(unused_variables)] - - #[allow(unused_mut)] - let mut res = ComponentAccessExpr::new(true); + config: &mut HandlerConfig + ) -> Result<(ComponentAccess, Self::State), InitError> { + let mut ca = ComponentAccess::new_true(); $( - let (expr, $q) = $Q::init(world, config)?; - - let Ok(expr) = res.and(&expr) else { - return Err(InitError(format!( - "conflicting access in tuple `{}`: tuple element `{}` conflicts with previous elements", - any::type_name::(), - any::type_name::<$Q>(), - ).into())); - }; - - res = expr; + let (this_ca, $q) = $Q::init(world, config)?; + ca = ca.and(&this_ca); )* - Ok((res, ($($q,)*))) + Ok((ca, ($($q,)*))) } fn new_state(world: &mut World) -> Self::State { @@ -250,13 +238,10 @@ unsafe impl Query for Option { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - let (mut expr, state) = Q::init(world, config)?; - - expr = expr.or(&ComponentAccessExpr::new(true)).unwrap(); - - Ok((expr, state)) + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + let (ca, state) = Q::init(world, config)?; + Ok((ComponentAccess::new_true().or(&ca), state)) } fn new_state(world: &mut World) -> Self::State { @@ -331,23 +316,14 @@ where fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - let (left_expr, left_state) = L::init(world, config)?; - let (right_expr, right_state) = R::init(world, config)?; - - let Ok(expr) = left_expr.or(&right_expr) else { - return Err(InitError( - format!( - "conflicting query in `{}` (both operands of an OR query may be active at the \ - same time)", - any::type_name::() - ) - .into(), - )); - }; + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + let (ca_lhs, state_lhs) = L::init(world, config)?; + let (ca_rhs, state_rhs) = R::init(world, config)?; - Ok((expr, (left_state, right_state))) + let ca_both = ca_lhs.and(&ca_rhs); + + Ok((ca_lhs.or(&ca_rhs).or(&ca_both), (state_lhs, state_rhs))) } fn new_state(world: &mut World) -> Self::State { @@ -433,12 +409,15 @@ where fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - let (left_expr, left_state) = L::init(world, config)?; - let (right_expr, right_state) = R::init(world, config)?; + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + let (ca_lhs, state_lhs) = L::init(world, config)?; + let (ca_rhs, state_rhs) = R::init(world, config)?; - Ok((left_expr.xor(&right_expr), (left_state, right_state))) + Ok(( + ca_lhs.and(&ca_rhs.not()).or(&ca_rhs.and(&ca_lhs.not())), + (state_lhs, state_rhs), + )) } fn new_state(world: &mut World) -> Self::State { @@ -511,11 +490,11 @@ unsafe impl Query for Not { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - let (expr, state) = Q::init(world, config)?; + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + let (ca, state) = Q::init(world, config)?; - Ok((expr.not(), state)) + Ok((ca.not(), state)) } fn new_state(world: &mut World) -> Self::State { @@ -581,13 +560,13 @@ unsafe impl Query for With { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - let (mut expr, state) = Q::init(world, config)?; + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + let (mut ca, state) = Q::init(world, config)?; - expr.access.clear(); + ca.clear_access(); - Ok((expr, state)) + Ok((ca, state)) } fn new_state(world: &mut World) -> Self::State { @@ -661,11 +640,11 @@ unsafe impl Query for Has { fn init( world: &mut World, - config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { + config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { let (_, state) = Q::init(world, config)?; - Ok((ComponentAccessExpr::new(true), state)) + Ok((ComponentAccess::new_true(), state)) } fn new_state(world: &mut World) -> Self::State { @@ -693,9 +672,9 @@ unsafe impl Query for EntityId { fn init( _world: &mut World, - _config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - Ok((ComponentAccessExpr::new(true), ())) + _config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + Ok((ComponentAccess::new_true(), ())) } fn new_state(_world: &mut World) -> Self::State {} @@ -723,9 +702,9 @@ unsafe impl Query for PhantomData { fn init( _world: &mut World, - _config: &mut Config, - ) -> Result<(ComponentAccessExpr, Self::State), InitError> { - Ok((ComponentAccessExpr::new(true), ())) + _config: &mut HandlerConfig, + ) -> Result<(ComponentAccess, Self::State), InitError> { + Ok((ComponentAccess::new_true(), ())) } fn new_state(_world: &mut World) -> Self::State {} @@ -774,27 +753,14 @@ mod tests { #[derive(Event)] struct E; - fn check_query() -> bool { - let r = std::panic::catch_unwind(|| { - let mut world = World::new(); - - (world.add_handler(|_: Receiver, _: Fetcher| {}), world) - }); - - if let Ok((_, mut world)) = r { - world.send(E); - true - } else { - false - } - } - /// Test for query access conflicts. macro_rules! t { ($name:ident, $succeed:expr, $Q:ty) => { #[test] fn $name() { - assert_eq!(check_query::<$Q>(), $succeed); + let mut world = World::new(); + let res = world.try_add_handler(|_: Receiver, _: Fetcher<$Q>| {}); + assert_eq!(res.is_ok(), $succeed, "{res:?}"); } }; } @@ -825,6 +791,18 @@ mod tests { t!(t14, true, (Option<&A>, &A, &A)); t!(t15, false, (Xor<(&A, &B), (&B, &C)>, &mut B)); t!(t16, true, (Xor<(&A, &B), (&B, &C)>, &B)); + t!( + t17, + true, + Or), (&A, Not<&B>)>, (&A, Not<&B>)> + ); + t!( + t18, + true, + (((&mut A, With<&B>), (&A, Not<&B>)), (&A, Not<&B>)) + ); + t!(t19, true, (&mut A, &A, Not<&A>)); + t!(t20, true, (Not<&A>, &mut A, &A)); #[test] #[allow(dead_code)] diff --git a/src/world.rs b/src/world.rs index dd9fdf1..5e9d711 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,14 +1,16 @@ //! Defines the [`World`] and related APIs. #[cfg(not(feature = "std"))] -use alloc::{vec, vec::Vec}; +use alloc::{format, string::String, vec, vec::Vec}; use core::alloc::Layout; use core::any::{self, TypeId}; use core::cell::UnsafeCell; +use core::fmt::Write; use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; +use crate::access::ComponentAccess; use crate::archetype::Archetypes; use crate::assert::{AssertMutable, UnwrapDebugChecked}; use crate::component::{ @@ -22,8 +24,8 @@ use crate::event::{ EventPtr, EventQueue, Events, Insert, Remove, RemoveEvent, Spawn, SpawnQueued, }; use crate::handler::{ - AddHandler, Config, Handler, HandlerId, HandlerInfo, HandlerInfoInner, HandlerList, Handlers, - IntoHandler, RemoveHandler, + AddHandler, Handler, HandlerConfig, HandlerId, HandlerInfo, HandlerInfoInner, HandlerList, + Handlers, IntoHandler, MaybeInvalidAccess, ReceivedEventId, RemoveHandler, }; /// A container for all data in the ECS. This includes entities, components, @@ -288,6 +290,118 @@ impl World { Some(unsafe { &mut *col.data().as_ptr().cast::().add(loc.row.0 as usize) }) } + pub(crate) fn try_add_handler, M>( + &mut self, + handler: H, + ) -> Result { + let mut handler = handler.into_handler(); + let mut config = HandlerConfig::default(); + + let type_id = handler.type_id(); + + if let Some(type_id) = type_id { + if let Some(info) = self.handlers.get_by_type_id(type_id) { + return Ok(info.id()); + } + } + + let handler_name = handler.name(); + + if let Err(e) = handler.init(self, &mut config) { + return Err(format!("initialization of {handler_name} failed: {e}")); + } + + let received_event = match config.received_event { + ReceivedEventId::None => { + return Err(format!( + "handler {handler_name} did not specify an event to receive" + )); + } + ReceivedEventId::Ok(event) => event, + ReceivedEventId::Invalid => { + return Err(format!( + "handler {handler_name} attempted to listen for more than one event type" + )) + } + }; + + let received_event_access = match config.received_event_access { + MaybeInvalidAccess::Ok(access) => access, + MaybeInvalidAccess::Invalid => { + return Err(format!( + "handler {handler_name} has conflicting access to the received event" + )) + } + }; + + let event_queue_access = match config.event_queue_access { + MaybeInvalidAccess::Ok(access) => access, + MaybeInvalidAccess::Invalid => { + return Err(format!( + "handler {handler_name} has conflicting access to the event queue" + )); + } + }; + + let component_access_conjunction = config + .component_accesses + .iter() + .fold(ComponentAccess::new_true(), |acc, a| acc.and(a)); + + let conflicts = component_access_conjunction.collect_conflicts(); + + if !conflicts.is_empty() { + let mut errmsg = format!( + "handler {handler_name} contains conflicting component access (aliased \ + mutability)\nconflicting components are...\n" + ); + + for idx in conflicts { + errmsg += "- "; + match self.components.get_by_index(idx) { + Some(info) => errmsg += info.name(), + None => { + write!(&mut errmsg, "{idx:?}").unwrap(); + } + }; + } + + return Err(errmsg); + } + + let component_access_disjunction = config + .component_accesses + .iter() + .fold(ComponentAccess::new_false(), |acc, a| acc.or(a)); + + let info = HandlerInfo::new(HandlerInfoInner { + name: handler_name, + id: HandlerId::NULL, // Filled in later. + type_id, + order: 0, // Filled in later. + received_event, + received_event_access, + targeted_event_component_access: config.targeted_event_component_access, + sent_untargeted_events: config.sent_untargeted_events, + sent_targeted_events: config.sent_targeted_events, + event_queue_access, + component_access: component_access_conjunction, + archetype_filter: component_access_disjunction, + referenced_components: config.referenced_components, + priority: config.priority, + handler, + }); + + let id = self.handlers.add(info); + let info = self.handlers.get_mut(id).unwrap(); + + self.archetypes.register_handler(info); + + self.send(AddHandler(id)); + + Ok(id) + } + /// Adds a new handler to the world, returns its [`HandlerId`], and sends /// the [`AddHandler`] event to signal its creation. /// @@ -326,54 +440,10 @@ impl World { /// [`Handler::type_id`]: crate::handler::Handler::type_id #[track_caller] pub fn add_handler, M>(&mut self, handler: H) -> HandlerId { - let mut handler = handler.into_handler(); - let mut config = Config::default(); - - let type_id = handler.type_id(); - - if let Some(type_id) = type_id { - if let Some(info) = self.handlers.get_by_type_id(type_id) { - return info.id(); - } + match self.try_add_handler(handler) { + Ok(id) => id, + Err(e) => panic!("{e}"), } - - if let Err(e) = handler.init(self, &mut config) { - panic!("{e}"); - } - - let Some(received_event) = config.received_event else { - panic!( - "handler `{}` did not specify an event to receive. All handlers must listen for \ - exactly one event type (see `Receiver`)", - any::type_name::() - ) - }; - - let info = HandlerInfo::new(HandlerInfoInner { - name: handler.name(), - id: HandlerId::NULL, // Filled in later. - type_id, - order: 0, // Filled in later. - received_event, - received_event_access: config.received_event_access, - targeted_event_expr: config.targeted_event_expr, - sent_untargeted_events: config.sent_untargeted_events, - sent_targeted_events: config.sent_targeted_events, - event_queue_access: config.event_queue_access, - component_access: config.component_access, - referenced_components: config.referenced_components, - priority: config.priority, - handler, - }); - - let id = self.handlers.add(info); - let info = self.handlers.get_mut(id).unwrap(); - - self.archetypes.register_handler(info); - - self.send(AddHandler(id)); - - id } /// Removes a handler from the world, returns its [`HandlerInfo`], and sends @@ -526,7 +596,7 @@ impl World { let mut handlers_to_remove = vec![]; for handler in self.handlers.iter() { - if handler.referenced_components().contains(component.index()) { + if handler.references_component(component.index()) { handlers_to_remove.push(handler.id()); } } @@ -684,8 +754,10 @@ impl World { for handler in self.handlers.iter() { if handler.received_event() == event || match event.index() { - EventIdx::Targeted(idx) => handler.sent_targeted_events().contains(idx), - EventIdx::Untargeted(idx) => handler.sent_untargeted_events().contains(idx), + EventIdx::Targeted(idx) => handler.sent_targeted_events_bitset().contains(idx), + EventIdx::Untargeted(idx) => { + handler.sent_untargeted_events_bitset().contains(idx) + } } { to_remove.push(handler.id()); diff --git a/tutorial/ch01_handlers_and_events.md b/tutorial/ch01_handlers_and_events.md index 67b22d4..11bc20a 100644 --- a/tutorial/ch01_handlers_and_events.md +++ b/tutorial/ch01_handlers_and_events.md @@ -61,10 +61,10 @@ world.add_handler(|| {}); When multiple handlers listen for the same event, we'll need to consider the order those handlers should run when the event is sent. -Handler order is first determined by the handler's [`Priority`]. This is a enum with three states: `High`, `Medium`, and `Low`. `Medium` is the default. +Handler order is first determined by the handler's [`HandlerPriority`]. This is a enum with three states: `High`, `Medium`, and `Low`. `Medium` is the default. If handlers have the same priority, then we fall back on the order the handlers were added to the `World` to decide the order. -[`Priority`]: crate::handler::Priority +[`HandlerPriority`]: crate::handler::HandlerPriority ```rust use evenio::prelude::*;