use alloc::{boxed::Box, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::{
any::TypeId,
fmt::{self, Debug},
ops::{Index, IndexMut, Range},
};
use bevy_platform::collections::HashMap;
use slotmap::{new_key_type, Key, KeyData, SecondaryMap, SlotMap};
use crate::{
component::{CheckChangeTicks, Tick},
prelude::{SystemIn, SystemSet},
query::FilteredAccessSet,
schedule::{
graph::{Direction, GraphNodeId},
BoxedCondition, InternedSystemSet,
},
system::{
ReadOnlySystem, RunSystemError, ScheduleSystem, System, SystemParamValidationError,
SystemStateFlags,
},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
pub(crate) struct SystemNode {
pub(crate) inner: Option<SystemWithAccess>,
}
pub struct SystemWithAccess {
pub system: ScheduleSystem,
pub access: FilteredAccessSet,
}
impl SystemWithAccess {
pub fn new(system: ScheduleSystem) -> Self {
Self {
system,
access: FilteredAccessSet::new(),
}
}
}
impl System for SystemWithAccess {
type In = ();
type Out = ();
#[inline]
fn name(&self) -> DebugName {
self.system.name()
}
#[inline]
fn type_id(&self) -> TypeId {
self.system.type_id()
}
#[inline]
fn flags(&self) -> SystemStateFlags {
self.system.flags()
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Result<Self::Out, RunSystemError> {
unsafe { self.system.run_unsafe(input, world) }
}
#[cfg(feature = "hotpatching")]
#[inline]
fn refresh_hotpatch(&mut self) {
self.system.refresh_hotpatch();
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
self.system.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, world: DeferredWorld) {
self.system.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
unsafe { self.system.validate_param_unsafe(world) }
}
#[inline]
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
self.system.initialize(world)
}
#[inline]
fn check_change_tick(&mut self, check: CheckChangeTicks) {
self.system.check_change_tick(check);
}
#[inline]
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
self.system.default_system_sets()
}
#[inline]
fn get_last_run(&self) -> Tick {
self.system.get_last_run()
}
#[inline]
fn set_last_run(&mut self, last_run: Tick) {
self.system.set_last_run(last_run);
}
}
pub struct ConditionWithAccess {
pub condition: BoxedCondition,
pub access: FilteredAccessSet,
}
impl ConditionWithAccess {
pub const fn new(condition: BoxedCondition) -> Self {
Self {
condition,
access: FilteredAccessSet::new(),
}
}
}
impl System for ConditionWithAccess {
type In = ();
type Out = bool;
#[inline]
fn name(&self) -> DebugName {
self.condition.name()
}
#[inline]
fn type_id(&self) -> TypeId {
self.condition.type_id()
}
#[inline]
fn flags(&self) -> SystemStateFlags {
self.condition.flags()
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Result<Self::Out, RunSystemError> {
unsafe { self.condition.run_unsafe(input, world) }
}
#[cfg(feature = "hotpatching")]
#[inline]
fn refresh_hotpatch(&mut self) {
self.condition.refresh_hotpatch();
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
self.condition.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, world: DeferredWorld) {
self.condition.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
unsafe { self.condition.validate_param_unsafe(world) }
}
#[inline]
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
self.condition.initialize(world)
}
#[inline]
fn check_change_tick(&mut self, check: CheckChangeTicks) {
self.condition.check_change_tick(check);
}
#[inline]
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
self.condition.default_system_sets()
}
#[inline]
fn get_last_run(&self) -> Tick {
self.condition.get_last_run()
}
#[inline]
fn set_last_run(&mut self, last_run: Tick) {
self.condition.set_last_run(last_run);
}
}
impl SystemNode {
pub fn new(system: ScheduleSystem) -> Self {
Self {
inner: Some(SystemWithAccess::new(system)),
}
}
pub fn get(&self) -> Option<&SystemWithAccess> {
self.inner.as_ref()
}
pub fn get_mut(&mut self) -> Option<&mut SystemWithAccess> {
self.inner.as_mut()
}
}
new_key_type! {
pub struct SystemKey;
pub struct SystemSetKey;
}
impl GraphNodeId for SystemKey {
type Adjacent = (SystemKey, Direction);
type Edge = (SystemKey, SystemKey);
fn kind(&self) -> &'static str {
"system"
}
}
impl GraphNodeId for SystemSetKey {
type Adjacent = (SystemSetKey, Direction);
type Edge = (SystemSetKey, SystemSetKey);
fn kind(&self) -> &'static str {
"system set"
}
}
impl TryFrom<NodeId> for SystemKey {
type Error = SystemSetKey;
fn try_from(value: NodeId) -> Result<Self, Self::Error> {
match value {
NodeId::System(key) => Ok(key),
NodeId::Set(key) => Err(key),
}
}
}
impl TryFrom<NodeId> for SystemSetKey {
type Error = SystemKey;
fn try_from(value: NodeId) -> Result<Self, Self::Error> {
match value {
NodeId::System(key) => Err(key),
NodeId::Set(key) => Ok(key),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NodeId {
System(SystemKey),
Set(SystemSetKey),
}
impl NodeId {
pub const fn is_system(&self) -> bool {
matches!(self, NodeId::System(_))
}
pub const fn is_set(&self) -> bool {
matches!(self, NodeId::Set(_))
}
pub const fn as_system(&self) -> Option<SystemKey> {
match self {
NodeId::System(system) => Some(*system),
NodeId::Set(_) => None,
}
}
pub const fn as_set(&self) -> Option<SystemSetKey> {
match self {
NodeId::System(_) => None,
NodeId::Set(set) => Some(*set),
}
}
}
impl GraphNodeId for NodeId {
type Adjacent = CompactNodeIdAndDirection;
type Edge = CompactNodeIdPair;
fn kind(&self) -> &'static str {
match self {
NodeId::System(n) => n.kind(),
NodeId::Set(n) => n.kind(),
}
}
}
impl From<SystemKey> for NodeId {
fn from(system: SystemKey) -> Self {
NodeId::System(system)
}
}
impl From<SystemSetKey> for NodeId {
fn from(set: SystemSetKey) -> Self {
NodeId::Set(set)
}
}
#[derive(Clone, Copy)]
pub struct CompactNodeIdAndDirection {
key: KeyData,
is_system: bool,
direction: Direction,
}
impl Debug for CompactNodeIdAndDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tuple: (_, _) = (*self).into();
tuple.fmt(f)
}
}
impl From<(NodeId, Direction)> for CompactNodeIdAndDirection {
fn from((id, direction): (NodeId, Direction)) -> Self {
let key = match id {
NodeId::System(key) => key.data(),
NodeId::Set(key) => key.data(),
};
let is_system = id.is_system();
Self {
key,
is_system,
direction,
}
}
}
impl From<CompactNodeIdAndDirection> for (NodeId, Direction) {
fn from(value: CompactNodeIdAndDirection) -> Self {
let node = match value.is_system {
true => NodeId::System(value.key.into()),
false => NodeId::Set(value.key.into()),
};
(node, value.direction)
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct CompactNodeIdPair {
key_a: KeyData,
key_b: KeyData,
is_system_a: bool,
is_system_b: bool,
}
impl Debug for CompactNodeIdPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tuple: (_, _) = (*self).into();
tuple.fmt(f)
}
}
impl From<(NodeId, NodeId)> for CompactNodeIdPair {
fn from((a, b): (NodeId, NodeId)) -> Self {
let key_a = match a {
NodeId::System(index) => index.data(),
NodeId::Set(index) => index.data(),
};
let is_system_a = a.is_system();
let key_b = match b {
NodeId::System(index) => index.data(),
NodeId::Set(index) => index.data(),
};
let is_system_b = b.is_system();
Self {
key_a,
key_b,
is_system_a,
is_system_b,
}
}
}
impl From<CompactNodeIdPair> for (NodeId, NodeId) {
fn from(value: CompactNodeIdPair) -> Self {
let a = match value.is_system_a {
true => NodeId::System(value.key_a.into()),
false => NodeId::Set(value.key_a.into()),
};
let b = match value.is_system_b {
true => NodeId::System(value.key_b.into()),
false => NodeId::Set(value.key_b.into()),
};
(a, b)
}
}
#[derive(Default)]
pub struct Systems {
nodes: SlotMap<SystemKey, SystemNode>,
conditions: SecondaryMap<SystemKey, Vec<ConditionWithAccess>>,
uninit: Vec<SystemKey>,
}
impl Systems {
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn get(&self, key: SystemKey) -> Option<&SystemWithAccess> {
self.nodes.get(key).and_then(|node| node.get())
}
pub fn get_mut(&mut self, key: SystemKey) -> Option<&mut SystemWithAccess> {
self.nodes.get_mut(key).and_then(|node| node.get_mut())
}
pub(crate) fn node_mut(&mut self, key: SystemKey) -> &mut SystemNode {
&mut self.nodes[key]
}
pub fn has_conditions(&self, key: SystemKey) -> bool {
self.conditions
.get(key)
.is_some_and(|conditions| !conditions.is_empty())
}
pub fn get_conditions(&self, key: SystemKey) -> Option<&[ConditionWithAccess]> {
self.conditions.get(key).map(Vec::as_slice)
}
pub fn get_conditions_mut(&mut self, key: SystemKey) -> Option<&mut Vec<ConditionWithAccess>> {
self.conditions.get_mut(key)
}
pub fn iter(
&self,
) -> impl Iterator<Item = (SystemKey, &ScheduleSystem, &[ConditionWithAccess])> + '_ {
self.nodes.iter().filter_map(|(key, node)| {
let system = &node.get()?.system;
let conditions = self
.conditions
.get(key)
.map(Vec::as_slice)
.unwrap_or_default();
Some((key, system, conditions))
})
}
pub fn insert(
&mut self,
system: ScheduleSystem,
conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
) -> SystemKey {
let key = self.nodes.insert(SystemNode::new(system));
self.conditions.insert(
key,
conditions
.into_iter()
.map(ConditionWithAccess::new)
.collect(),
);
self.uninit.push(key);
key
}
pub fn is_initialized(&self) -> bool {
self.uninit.is_empty()
}
pub fn initialize(&mut self, world: &mut World) {
for key in self.uninit.drain(..) {
let Some(system) = self.nodes.get_mut(key).and_then(|node| node.get_mut()) else {
continue;
};
system.access = system.system.initialize(world);
let Some(conditions) = self.conditions.get_mut(key) else {
continue;
};
for condition in conditions {
condition.access = condition.condition.initialize(world);
}
}
}
}
impl Index<SystemKey> for Systems {
type Output = SystemWithAccess;
#[track_caller]
fn index(&self, key: SystemKey) -> &Self::Output {
self.get(key)
.unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
}
}
impl IndexMut<SystemKey> for Systems {
#[track_caller]
fn index_mut(&mut self, key: SystemKey) -> &mut Self::Output {
self.get_mut(key)
.unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
}
}
#[derive(Default)]
pub struct SystemSets {
sets: SlotMap<SystemSetKey, InternedSystemSet>,
conditions: SecondaryMap<SystemSetKey, Vec<ConditionWithAccess>>,
ids: HashMap<InternedSystemSet, SystemSetKey>,
uninit: Vec<UninitializedSet>,
}
struct UninitializedSet {
key: SystemSetKey,
uninitialized_conditions: Range<usize>,
}
impl SystemSets {
pub fn len(&self) -> usize {
self.sets.len()
}
pub fn is_empty(&self) -> bool {
self.sets.is_empty()
}
pub fn contains(&self, set: impl SystemSet) -> bool {
self.ids.contains_key(&set.intern())
}
pub fn get(&self, key: SystemSetKey) -> Option<&dyn SystemSet> {
self.sets.get(key).map(|set| &**set)
}
pub fn get_key_or_insert(&mut self, set: InternedSystemSet) -> SystemSetKey {
*self.ids.entry(set).or_insert_with(|| {
let key = self.sets.insert(set);
self.conditions.insert(key, Vec::new());
key
})
}
pub fn has_conditions(&self, key: SystemSetKey) -> bool {
self.conditions
.get(key)
.is_some_and(|conditions| !conditions.is_empty())
}
pub fn get_conditions(&self, key: SystemSetKey) -> Option<&[ConditionWithAccess]> {
self.conditions.get(key).map(Vec::as_slice)
}
pub fn get_conditions_mut(
&mut self,
key: SystemSetKey,
) -> Option<&mut Vec<ConditionWithAccess>> {
self.conditions.get_mut(key)
}
pub fn iter(
&self,
) -> impl Iterator<Item = (SystemSetKey, &dyn SystemSet, &[ConditionWithAccess])> {
self.sets.iter().filter_map(|(key, set)| {
let conditions = self.conditions.get(key)?.as_slice();
Some((key, &**set, conditions))
})
}
pub fn insert(
&mut self,
set: InternedSystemSet,
new_conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
) -> SystemSetKey {
let key = self.get_key_or_insert(set);
if !new_conditions.is_empty() {
let current_conditions = &mut self.conditions[key];
let start = current_conditions.len();
self.uninit.push(UninitializedSet {
key,
uninitialized_conditions: start..(start + new_conditions.len()),
});
current_conditions.extend(new_conditions.into_iter().map(ConditionWithAccess::new));
}
key
}
pub fn is_initialized(&self) -> bool {
self.uninit.is_empty()
}
pub fn initialize(&mut self, world: &mut World) {
for uninit in self.uninit.drain(..) {
let Some(conditions) = self.conditions.get_mut(uninit.key) else {
continue;
};
for condition in &mut conditions[uninit.uninitialized_conditions] {
condition.access = condition.initialize(world);
}
}
}
}
impl Index<SystemSetKey> for SystemSets {
type Output = dyn SystemSet;
#[track_caller]
fn index(&self, key: SystemSetKey) -> &Self::Output {
self.get(key).unwrap_or_else(|| {
panic!(
"System set with key {:?} does not exist in the schedule",
key
)
})
}
}
#[cfg(test)]
mod tests {
use alloc::{boxed::Box, vec};
use crate::{
prelude::SystemSet,
schedule::{SystemSets, Systems},
system::IntoSystem,
world::World,
};
#[derive(SystemSet, Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct TestSet;
#[test]
fn systems() {
fn empty_system() {}
let mut systems = Systems::default();
assert!(systems.is_empty());
assert_eq!(systems.len(), 0);
let system = Box::new(IntoSystem::into_system(empty_system));
let key = systems.insert(system, vec![]);
assert!(!systems.is_empty());
assert_eq!(systems.len(), 1);
assert!(systems.get(key).is_some());
assert!(systems.get_conditions(key).is_some());
assert!(systems.get_conditions(key).unwrap().is_empty());
assert!(systems.get_mut(key).is_some());
assert!(!systems.is_initialized());
assert!(systems.iter().next().is_some());
let mut world = World::new();
systems.initialize(&mut world);
assert!(systems.is_initialized());
}
#[test]
fn system_sets() {
fn always_true() -> bool {
true
}
let mut sets = SystemSets::default();
assert!(sets.is_empty());
assert_eq!(sets.len(), 0);
let condition = Box::new(IntoSystem::into_system(always_true));
let key = sets.insert(TestSet.intern(), vec![condition]);
assert!(!sets.is_empty());
assert_eq!(sets.len(), 1);
assert!(sets.get(key).is_some());
assert!(sets.get_conditions(key).is_some());
assert!(!sets.get_conditions(key).unwrap().is_empty());
assert!(!sets.is_initialized());
assert!(sets.iter().next().is_some());
let mut world = World::new();
sets.initialize(&mut world);
assert!(sets.is_initialized());
}
}