use crate::error::Result;
use crate::world::{UnsafeWorldCell, World};
use std::any::TypeId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SystemId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ComponentId(pub TypeId);
impl ComponentId {
pub fn of<T: 'static>() -> Self {
Self(TypeId::of::<T>())
}
}
impl From<TypeId> for ComponentId {
fn from(id: TypeId) -> Self {
Self(id)
}
}
#[derive(Debug, Clone)]
pub struct SystemAccess {
pub reads: Vec<ComponentId>,
pub writes: Vec<ComponentId>,
}
impl Default for SystemAccess {
fn default() -> Self {
Self::new()
}
}
impl SystemAccess {
pub fn empty() -> Self {
Self {
reads: Vec::new(),
writes: Vec::new(),
}
}
pub fn new() -> Self {
Self::empty()
}
pub fn read<T: 'static>(mut self) -> Self {
self.reads.push(ComponentId::of::<T>());
self
}
pub fn write<T: 'static>(mut self) -> Self {
self.writes.push(ComponentId::of::<T>());
self
}
pub fn merge(&self, other: &SystemAccess) -> SystemAccess {
let mut reads = Vec::with_capacity(self.reads.len() + other.reads.len());
let mut writes = Vec::with_capacity(self.writes.len() + other.writes.len());
reads.extend_from_slice(&self.reads);
writes.extend_from_slice(&self.writes);
for read in &other.reads {
if !reads.contains(read) {
reads.push(*read);
}
}
for write in &other.writes {
if !writes.contains(write) {
writes.push(*write);
}
}
SystemAccess { reads, writes }
}
pub fn conflicts_with(&self, other: &SystemAccess) -> bool {
for write in &self.writes {
if other.writes.contains(write) {
return true;
}
if other.reads.contains(write) {
return true;
}
}
for write in &other.writes {
if self.reads.contains(write) {
return true;
}
}
false
}
pub fn can_run_parallel(&self, other: &SystemAccess) -> bool {
!self.conflicts_with(other)
}
pub fn resource<R: 'static>(mut self) -> Self {
self.reads.push(ComponentId::of::<R>());
self
}
pub fn resource_mut<R: 'static>(mut self) -> Self {
self.writes.push(ComponentId::of::<R>());
self
}
}
pub trait System: Send + Sync {
fn accesses(&self) -> SystemAccess;
fn name(&self) -> &'static str;
fn run(
&mut self,
world: &mut World,
commands: &mut crate::command::CommandBuffer,
) -> Result<()>;
unsafe fn run_parallel(
&mut self,
world: UnsafeWorldCell,
commands: &mut crate::command::CommandBuffer,
) -> Result<()> {
let world_mut = &mut *world.world_ptr();
self.run(world_mut, commands)
}
}
pub type BoxedSystem = Box<dyn System>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_access_conflicts() {
let mut access1 = SystemAccess::empty();
access1.writes.push(ComponentId::of::<i32>());
let mut access2 = SystemAccess::empty();
access2.writes.push(ComponentId::of::<i32>());
assert!(access1.conflicts_with(&access2));
}
#[test]
fn test_system_access_no_conflicts() {
let mut access1 = SystemAccess::empty();
access1.reads.push(ComponentId::of::<i32>());
let mut access2 = SystemAccess::empty();
access2.reads.push(ComponentId::of::<i32>());
assert!(!access1.conflicts_with(&access2));
}
#[derive(Default)]
struct DummySystem;
impl System for DummySystem {
fn accesses(&self) -> SystemAccess {
SystemAccess::empty()
}
fn name(&self) -> &'static str {
"dummy_system"
}
fn run(
&mut self,
world: &mut World,
_commands: &mut crate::command::CommandBuffer,
) -> Result<()> {
let entity = world.spawn_entity((42i32,));
world.despawn(entity).ok();
Ok(())
}
}
#[test]
fn test_system_run_signature() {
let mut world = World::new();
let mut commands = crate::command::CommandBuffer::new();
let mut system = DummySystem;
system
.run(&mut world, &mut commands)
.expect("system should run");
}
}