use std::borrow::Cow;
use bit_set::BitSet;
use super::{
entity::Entity,
entry::{EntryMut, EntryRef},
permissions::Permissions,
query::{
filter::EntityFilter,
view::{IntoView, View},
Query,
},
storage::{archetype::ArchetypeIndex, component::ComponentTypeId},
world::{EntityAccessError, EntityStore, StorageAccessor, World, WorldId},
};
pub enum ArchetypeAccess {
All,
Some(BitSet),
}
impl ArchetypeAccess {
pub fn is_disjoint(&self, other: &ArchetypeAccess) -> bool {
match self {
Self::All => false,
Self::Some(mine) => {
match other {
Self::All => false,
Self::Some(theirs) => mine.is_disjoint(theirs),
}
}
}
}
pub fn bitset(&self) -> Option<&BitSet> {
match self {
Self::All => None,
Self::Some(bitset) => Some(bitset),
}
}
}
#[derive(Clone, Debug)]
pub enum ComponentAccess<'a> {
All,
Allow(Cow<'a, Permissions<ComponentTypeId>>),
Disallow(Cow<'a, Permissions<ComponentTypeId>>),
}
impl<'a> ComponentAccess<'a> {
pub fn allows_read(&self, component: ComponentTypeId) -> bool {
match self {
Self::All => true,
Self::Allow(components) => components.reads().contains(&component),
Self::Disallow(components) => !components.reads().contains(&component),
}
}
pub fn allows_write(&self, component: ComponentTypeId) -> bool {
match self {
Self::All => true,
Self::Allow(components) => components.writes().contains(&component),
Self::Disallow(components) => !components.writes().contains(&component),
}
}
pub(crate) fn split(&mut self, access: Permissions<ComponentTypeId>) -> (Self, Self) {
fn append_incompatible(
denied: &mut Permissions<ComponentTypeId>,
to_deny: &Permissions<ComponentTypeId>,
) {
for read in to_deny.reads() {
denied.push_write(*read);
}
for write in to_deny.writes() {
denied.push(*write);
}
}
fn incompatible(
permissions: &Permissions<ComponentTypeId>,
) -> Permissions<ComponentTypeId> {
let mut denied = Permissions::new();
for read in permissions.reads_only() {
denied.push_write(*read);
}
for write in permissions.writes() {
denied.push(*write);
}
denied
}
match self {
Self::All => {
let denied = incompatible(&access);
(
Self::Allow(Cow::Owned(access)),
Self::Disallow(Cow::Owned(denied)),
)
}
Self::Allow(allowed) => {
if !allowed.is_superset(&access) {
panic!("view accesses components unavailable in this world: world allows only {}, view requires {}", allowed, access);
}
let mut allowed = allowed.clone();
allowed.to_mut().subtract(&access);
(Self::Allow(Cow::Owned(access)), Self::Allow(allowed))
}
Self::Disallow(denied) => {
if !denied.is_disjoint(&access) {
panic!("view accesses components unavailable in this world: world disallows {}, view requires {}", denied, access);
}
let mut denied = denied.clone();
append_incompatible(denied.to_mut(), &access);
(Self::Allow(Cow::Owned(access)), Self::Disallow(denied))
}
}
}
}
#[derive(Clone)]
pub struct SubWorld<'a> {
world: &'a World,
components: ComponentAccess<'a>,
archetypes: Option<&'a BitSet>,
}
impl<'a> SubWorld<'a> {
pub unsafe fn new_unchecked(
world: &'a World,
components: ComponentAccess<'a>,
archetypes: Option<&'a BitSet>,
) -> Self {
Self {
world,
components,
archetypes,
}
}
pub fn split<'b, T: IntoView>(&'b mut self) -> (SubWorld<'b>, SubWorld<'b>)
where
'a: 'b,
{
let permissions = T::View::requires_permissions();
let (left, right) = self.components.split(permissions);
(
SubWorld {
world: self.world,
components: left,
archetypes: self.archetypes,
},
SubWorld {
world: self.world,
components: right,
archetypes: self.archetypes,
},
)
}
pub fn split_for_query<'q, V: IntoView, F: EntityFilter>(
&mut self,
_: &'q Query<V, F>,
) -> (SubWorld, SubWorld) {
self.split::<V>()
}
fn validate_archetype_access(&self, ArchetypeIndex(arch_index): ArchetypeIndex) -> bool {
if let Some(archetypes) = self.archetypes {
archetypes.contains(arch_index as usize)
} else {
true
}
}
}
impl<'a> EntityStore for SubWorld<'a> {
fn get_component_storage<V: for<'b> View<'b>>(
&self,
) -> Result<StorageAccessor, EntityAccessError> {
if V::validate_access(&self.components) {
Ok(self
.world
.get_component_storage::<V>()
.unwrap()
.with_allowed_archetypes(self.archetypes))
} else {
Err(EntityAccessError::AccessDenied)
}
}
fn entry_ref(&self, entity: Entity) -> Result<EntryRef, EntityAccessError> {
let entry = self.world.entry_ref(entity)?;
if !self.validate_archetype_access(entry.location().archetype()) {
return Err(EntityAccessError::AccessDenied);
}
Ok(EntryRef {
allowed_components: self.components.clone(),
..entry
})
}
fn entry_mut(&mut self, entity: Entity) -> Result<EntryMut, EntityAccessError> {
let entry = unsafe { self.world.entry_unchecked(entity)? };
if !self.validate_archetype_access(entry.location().archetype()) {
return Err(EntityAccessError::AccessDenied);
}
Ok(EntryMut {
allowed_components: self.components.clone(),
..entry
})
}
fn id(&self) -> WorldId {
self.world.id()
}
}
impl<'a> From<&'a mut World> for SubWorld<'a> {
fn from(world: &'a mut World) -> Self {
Self {
world,
components: ComponentAccess::All,
archetypes: None,
}
}
}
#[cfg(test)]
mod tests {
use crate::{
internals::{
query::view::{read::Read, write::Write},
world::{EntityStore, World},
},
world::ComponentError,
};
#[test]
fn split_write_permissions() {
let mut world = World::default();
let entity = world.push((1usize, false));
let (mut left, mut right) = world.split::<Write<usize>>();
let mut left_entry = left.entry_mut(entity).unwrap();
let mut right_entry = right.entry_mut(entity).unwrap();
assert!(left_entry.get_component::<usize>().is_ok());
assert!(left_entry.get_component_mut::<usize>().is_ok());
assert!(matches!(
left_entry.get_component::<bool>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
left_entry.get_component_mut::<bool>(),
Err(ComponentError::Denied { .. })
));
assert!(right_entry.get_component::<bool>().is_ok());
assert!(right_entry.get_component_mut::<bool>().is_ok());
assert!(matches!(
right_entry.get_component::<usize>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
right_entry.get_component_mut::<usize>(),
Err(ComponentError::Denied { .. })
));
}
#[test]
fn split_read_permissions() {
let mut world = World::default();
let entity = world.push((1usize, false));
let (mut left, mut right) = world.split::<Read<usize>>();
let mut left_entry = left.entry_mut(entity).unwrap();
let mut right_entry = right.entry_mut(entity).unwrap();
assert!(left_entry.get_component::<usize>().is_ok());
assert!(matches!(
left_entry.get_component_mut::<usize>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
left_entry.get_component::<bool>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
left_entry.get_component_mut::<bool>(),
Err(ComponentError::Denied { .. })
));
assert!(right_entry.get_component::<bool>().is_ok());
assert!(right_entry.get_component_mut::<bool>().is_ok());
assert!(right_entry.get_component::<usize>().is_ok());
assert!(matches!(
right_entry.get_component_mut::<usize>(),
Err(ComponentError::Denied { .. })
));
}
#[test]
fn complex_split() {
struct A;
struct B;
struct C;
let mut world = World::default();
let entity = world.push((A, B, C));
let (mut left, mut right) = world.split::<(Read<A>, Write<B>)>();
let mut left_entry = left.entry_mut(entity).unwrap();
let mut right_entry = right.entry_mut(entity).unwrap();
assert!(left_entry.get_component::<A>().is_ok());
assert!(matches!(
left_entry.get_component_mut::<A>(),
Err(ComponentError::Denied { .. })
));
assert!(left_entry.get_component::<B>().is_ok());
assert!(left_entry.get_component_mut::<B>().is_ok());
assert!(matches!(
left_entry.get_component::<C>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
left_entry.get_component_mut::<C>(),
Err(ComponentError::Denied { .. })
));
assert!(right_entry.get_component::<A>().is_ok());
assert!(matches!(
right_entry.get_component_mut::<A>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
right_entry.get_component::<B>(),
Err(ComponentError::Denied { .. })
));
assert!(matches!(
right_entry.get_component_mut::<B>(),
Err(ComponentError::Denied { .. })
));
assert!(right_entry.get_component::<C>().is_ok());
assert!(right_entry.get_component_mut::<C>().is_ok());
}
}