summaryrefslogtreecommitdiff
path: root/ecs/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ecs/src/lib.rs')
-rw-r--r--ecs/src/lib.rs773
1 files changed, 0 insertions, 773 deletions
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
deleted file mode 100644
index 667aa0e..0000000
--- a/ecs/src/lib.rs
+++ /dev/null
@@ -1,773 +0,0 @@
-#![deny(clippy::all, clippy::pedantic)]
-
-use std::any::{Any, TypeId, type_name};
-use std::fmt::Debug;
-use std::hint::cold_path;
-use std::mem::ManuallyDrop;
-use std::rc::Rc;
-use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-
-use hashbrown::HashMap;
-
-use crate::actions::Action;
-use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent;
-use crate::component::storage::{EntityAlreadyExistsError, Storage as ComponentStorage};
-use crate::component::{
- Component,
- IntoParts as IntoComponentParts,
- Parts as ComponentParts,
- Sequence as ComponentSequence,
-};
-use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle};
-use crate::error::{
- ErrorHandler,
- Metadata as ErrorMetadata,
- SourceKind as ErrorSourceKind,
- err_handler_panic,
-};
-use crate::event::component::Added;
-use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter};
-use crate::extension::{Collector as ExtensionCollector, Extension};
-use crate::lock::Lock;
-use crate::pair::{ChildOf, Pair, Wildcard};
-use crate::phase::{
- HasSystem as PhaseHasSystem,
- POST_UPDATE as POST_UPDATE_PHASE,
- PRE_UPDATE as PRE_UPDATE_PHASE,
- Phase,
- START as START_PHASE,
- UPDATE as UPDATE_PHASE,
-};
-use crate::query::flexible::Query as FlexibleQuery;
-use crate::query::{
- MAX_TERM_CNT as QUERY_MAX_TERM_CNT,
- TermWithFieldTuple as QueryTermWithFieldTuple,
- TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
- Terms as QueryTerms,
- TermsBuilderInterface,
-};
-use crate::sole::{Single, Sole};
-use crate::stats::Stats;
-use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent};
-use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent};
-use crate::uid::{Kind as UidKind, Uid};
-
-pub mod actions;
-pub mod component;
-pub mod entity;
-pub mod error;
-pub mod event;
-pub mod extension;
-pub mod pair;
-pub mod phase;
-pub mod query;
-pub mod sole;
-pub mod stats;
-pub mod system;
-pub mod tuple;
-pub mod uid;
-pub mod util;
-
-mod lock;
-
-pub use ecs_macros::{Component, Sole};
-
-pub use crate::query::Query;
-
-#[derive(Debug)]
-pub struct World
-{
- data: WorldData,
- stop: AtomicBool,
- is_first_tick: AtomicBool,
- error_handler: ErrorHandler,
-}
-
-impl World
-{
- #[must_use]
- pub fn new() -> Self
- {
- let mut world = Self {
- data: WorldData::default(),
- stop: AtomicBool::new(false),
- is_first_tick: AtomicBool::new(false),
- error_handler: err_handler_panic,
- };
-
- crate::phase::spawn_entities(&mut world);
-
- world.add_sole(Stats::default()).ok();
-
- world
- }
-
- pub fn set_err_handler(&mut self, err_handler: ErrorHandler)
- {
- self.error_handler = err_handler;
- }
-
- /// Creates a entity with the given components. A new unique [`Uid`] will be generated
- /// for this entity.
- pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid
- where
- Comps: ComponentSequence,
- {
- let entity_uid = Uid::new_unique(UidKind::Entity);
-
- self.create_entity_with_uid(entity_uid, components);
-
- entity_uid
- }
-
- /// Creates a entity with the given components. The entity will have the specified
- /// [`Uid`].
- #[tracing::instrument(skip_all)]
- pub fn create_entity_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps)
- where
- Comps: ComponentSequence,
- {
- self.create_ent(entity_uid, components.into_parts_array());
- }
-
- pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts)
- {
- Self::add_entity_components(
- entity_id,
- [component_parts],
- &mut self.data.component_storage,
- &EventSubmitter::new(&self.data.new_events),
- );
- }
-
- pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration)
- {
- entity_decl.create(self);
- }
-
- /// Adds a globally shared singleton value.
- ///
- /// # Errors
- /// Returns `Err` if this [`Sole`] has already been added.
- pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
- where
- SoleT: Sole,
- {
- self.data.sole_storage.insert(sole)
- }
-
- pub fn register_observer<'this, SystemImpl, ObserverT>(
- &'this mut self,
- observer: ObserverT,
- ) where
- ObserverT: Observer<'this, SystemImpl>,
- {
- let (wrapper_comp, mut system_callbacks) = observer.finish_observer();
-
- let ent_id = Uid::new_unique(UidKind::Entity);
-
- self.create_ent(
- ent_id,
- [wrapper_comp.into_parts()].into_iter().chain(
- ObserverT::observed_events()
- .into_iter()
- .map(IntoComponentParts::into_parts),
- ),
- );
-
- system_callbacks.on_created(self, SystemMetadata { ent_id });
- }
-
- pub fn register_system<'this, SystemImpl>(
- &'this mut self,
- phase_euid: Uid,
- system: impl System<'this, SystemImpl>,
- )
- {
- let (type_erased_system, mut system_callbacks) = system.finish();
-
- let system_ent_id =
- self.create_entity((SystemComponent { system: type_erased_system },));
-
- system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });
-
- self.create_entity_with_uid(
- phase_euid,
- (Pair::builder()
- .relation::<PhaseHasSystem>()
- .target_id(system_ent_id)
- .build(),),
- );
- }
-
- /// Adds a extensions.
- pub fn add_extension(&mut self, extension: impl Extension)
- {
- let extension_collector = ExtensionCollector::new(self);
-
- extension.collect(extension_collector);
- }
-
- pub fn query<FieldTerms, FieldlessTerms>(
- &self,
- ) -> Query<'_, FieldTerms, FieldlessTerms>
- where
- FieldTerms: QueryTermWithFieldTuple,
- FieldlessTerms: QueryTermWithoutFieldTuple,
- {
- Query::new(self)
- }
-
- pub fn flexible_query<const MAX_TERM_CNT: usize>(
- &self,
- terms: QueryTerms<MAX_TERM_CNT>,
- ) -> FlexibleQuery<'_, MAX_TERM_CNT>
- {
- FlexibleQuery::new(self, terms)
- }
-
- pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>>
- {
- let archetype = self
- .data
- .component_storage
- .get_entity_archetype(entity_id)?;
-
- let Some(entity) = archetype.get_entity_by_id(entity_id) else {
- unreachable!("Should exist since archetype was found by entity id");
- };
-
- Some(EntityHandle::new(archetype, entity, self))
- }
-
- pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>>
- {
- Some(Single::new(self.data.sole_storage.get::<SoleT>()?))
- }
-
- pub fn event_submitter(&self) -> EventSubmitter<'_>
- {
- EventSubmitter::new(&self.data.new_events)
- }
-
- /// Performs a single tick.
- /// # Panics
- /// Will panic if mutable internal lock cannot be acquired.
- pub fn step(&mut self) -> StepResult
- {
- if self.stop.load(Ordering::Relaxed) {
- return StepResult::Stop;
- }
-
- if self
- .is_first_tick
- .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
- .is_ok()
- {
- let Some(start_phase_entity) = self.get_entity(*START_PHASE) else {
- unreachable!();
- };
-
- self.run_phase_systems(&start_phase_entity);
- }
-
- self.perform_phases();
-
- self.emit_new_events();
-
- self.data.component_storage.create_imaginary_archetypes();
-
- self.perform_queued_actions();
-
- if self.stop.load(Ordering::Relaxed) {
- return StepResult::Stop;
- }
-
- let Some(mut stats) = self.get_sole::<Stats>() else {
- unreachable!(); // Reason: is added in World::new
- };
-
- stats.current_tick += 1;
-
- StepResult::Continue
- }
-
- /// Starts a loop which calls [`Self::step`] until the world is stopped.
- pub fn start_loop(&mut self)
- {
- while let StepResult::Continue = self.step() {}
- }
-
- #[cfg(feature = "vizoxide")]
- pub fn create_vizoxide_archetype_graph(
- &self,
- name: impl AsRef<str>,
- ) -> Result<vizoxide::Graph, vizoxide::GraphvizError>
- {
- use std::borrow::Cow;
-
- use crate::component::storage::{
- VizoxideArchetypeGraphEdgeKind,
- VizoxideArchetypeGraphParams,
- };
-
- self.data.component_storage.create_vizoxide_archetype_graph(
- name,
- VizoxideArchetypeGraphParams {
- create_node_name: |archetype, _| {
- Cow::Owned(format!(
- "[{}]",
- archetype
- .component_ids_sorted()
- .into_iter()
- .map(|comp_id| comp_id.to_string())
- .collect::<Vec<_>>()
- .join(", ")
- ))
- },
- create_node_cb: |_archetype, archetype_metadata, node_builder| {
- if archetype_metadata.is_imaginary {
- return node_builder.attribute("shape", "ellipse");
- }
-
- node_builder.attribute("shape", "box")
- },
- create_edge_cb: |_, _, edge_kind, edge_builder| {
- edge_builder.attribute(
- "color",
- match edge_kind {
- VizoxideArchetypeGraphEdgeKind::Add => "green",
- VizoxideArchetypeGraphEdgeKind::Remove => "red",
- },
- )
- },
- },
- )
- }
-
- #[tracing::instrument(skip_all)]
- fn create_ent(
- &mut self,
- entity_uid: Uid,
- components: impl IntoIterator<Item = ComponentParts>,
- )
- {
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- if let Err(EntityAlreadyExistsError) =
- self.data.component_storage.create_entity(entity_uid)
- {
- // This is fine
- }
-
- Self::add_entity_components(
- entity_uid,
- components,
- &mut self.data.component_storage,
- &EventSubmitter::new(&self.data.new_events),
- );
- }
-
- fn run_phase_systems(&self, phase_entity: &EntityHandle<'_>)
- {
- // The phase's systems are retrieved this way so that the order they are
- // run is the same order as they were registered, even if they have local
- // components.
- for system_entity in phase_entity
- .get_wildcard_pair_matches::<PhaseHasSystem, Wildcard>()
- .into_iter()
- .filter_map(|phase_has_system| phase_has_system.get_target_ent())
- {
- let Some(system) = system_entity.get::<SystemComponent>() else {
- cold_path();
- continue;
- };
-
- // SAFETY: The world lives long enough
- if let Err(err) = unsafe {
- system
- .system
- .run(self, SystemMetadata { ent_id: system_entity.uid() })
- } {
- cold_path();
-
- (self.error_handler)(
- err,
- ErrorMetadata {
- source_name: system.system.name(),
- source_kind: ErrorSourceKind::System,
- },
- )
- }
- }
- }
-
- fn perform_child_phases(&self, parent_phase_euid: Uid)
- {
- let phase_query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([
- Phase::id(),
- Pair::builder()
- .relation::<ChildOf>()
- .target_id(parent_phase_euid)
- .build()
- .id(),
- ])
- .build(),
- );
-
- for child_phase_entity in &phase_query {
- self.run_phase_systems(&child_phase_entity);
- self.perform_child_phases(child_phase_entity.uid());
- }
- }
-
- fn perform_single_phase(&self, phase_entity_id: Uid)
- {
- let Some(phase_entity) = self.get_entity(phase_entity_id) else {
- unreachable!();
- };
-
- self.run_phase_systems(&phase_entity);
- self.perform_child_phases(phase_entity_id);
- }
-
- fn perform_phases(&self)
- {
- self.perform_single_phase(*PRE_UPDATE_PHASE);
- self.perform_single_phase(*UPDATE_PHASE);
- self.perform_single_phase(*POST_UPDATE_PHASE);
- }
-
- fn emit_new_events(&self)
- {
- loop {
- let new_events = {
- let mut new_events_lock = self
- .data
- .new_events
- .write_nonblock()
- .expect("Failed to acquire read-write lock to new events");
-
- if new_events_lock.is_empty() {
- break;
- }
-
- new_events_lock.take()
- };
-
- for (event_id, event_matches) in new_events {
- self.emit_event_observers(
- event_id,
- &EmittedEvent {
- event: event_id,
- match_ids: &event_matches.match_ids,
- },
- );
- }
- }
- }
-
- #[tracing::instrument(skip_all)]
- fn perform_queued_actions(&mut self)
- {
- let mut action_queue_lock = self
- .data
- .action_queue
- .queue
- .write_nonblock()
- .unwrap_or_else(|err| {
- panic!("Failed to take read-write action queue lock: {err}",);
- });
-
- for action in action_queue_lock.drain(..) {
- match action {
- Action::Spawn(new_entity_uid, components) => {
- if let Err(err) =
- self.data.component_storage.create_entity(new_entity_uid)
- {
- tracing::warn!("Failed to create entity: {err}");
- continue;
- }
-
- Self::add_entity_components(
- new_entity_uid,
- components,
- &mut self.data.component_storage,
- &EventSubmitter::new(&self.data.new_events),
- );
- }
- Action::Despawn(entity_uid) => {
- if let Err(err) =
- self.data.component_storage.remove_entity(entity_uid)
- {
- tracing::error!("Failed to despawn entity: {err}");
- }
- }
- Action::AddComponents(entity_uid, components) => {
- Self::add_entity_components(
- entity_uid,
- components,
- &mut self.data.component_storage,
- &EventSubmitter::new(&self.data.new_events),
- );
- }
- Action::RemoveComponents(entity_uid, component_ids) => {
- Self::remove_entity_components(
- entity_uid,
- component_ids,
- &mut self.data.component_storage,
- );
- }
- Action::Stop => {
- self.stop.store(true, Ordering::Relaxed);
- }
- }
- }
- }
-
- fn add_entity_components(
- entity_uid: Uid,
- components: impl IntoIterator<Item = ComponentParts>,
- component_storage: &mut ComponentStorage,
- event_submitter: &EventSubmitter<'_>,
- )
- {
- let component_iter = components.into_iter();
-
- for component_parts in component_iter {
- let comp_id = component_parts.id();
-
- let comp_name = component_parts.name();
-
- if let Err(err) = component_storage.add_entity_component(
- entity_uid,
- (comp_id, comp_name, component_parts.into_data()),
- ) {
- tracing::error!("Failed to add component {comp_name} to entity: {err}");
- continue;
- }
-
- if comp_id.kind() == UidKind::Pair {
- continue;
- }
-
- event_submitter.submit_event(
- &Pair::builder()
- .relation::<Added>()
- .target_id(comp_id)
- .build(),
- entity_uid,
- );
- }
- }
-
- fn remove_entity_components(
- entity_uid: Uid,
- component_ids: impl IntoIterator<Item = Uid>,
- component_storage: &mut ComponentStorage,
- )
- {
- let component_id_iter = component_ids.into_iter();
-
- for component_id in component_id_iter {
- if let Err(err) =
- component_storage.remove_entity_component(entity_uid, component_id)
- {
- tracing::error!("Failed to remove component to entity: {err}");
- }
- }
- }
-
- fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>)
- {
- assert_eq!(event_id.kind(), UidKind::Pair);
-
- let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query(
- self.flexible_query(
- QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
- .with_required([ObserverWrapperComponent::id(), event_id])
- .build(),
- ),
- );
-
- for (observer_ent_id, (observer,)) in query.iter_with_euids() {
- if let Err(err) = unsafe {
- observer.run(
- self,
- SystemMetadata { ent_id: observer_ent_id },
- emitted_event.clone(),
- )
- } {
- cold_path();
-
- (self.error_handler)(
- err,
- ErrorMetadata {
- source_name: observer.name(),
- source_kind: ErrorSourceKind::Observer,
- },
- )
- }
- }
- }
-}
-
-impl Default for World
-{
- fn default() -> Self
- {
- Self::new()
- }
-}
-
-/// The result of calling [`World::step`].
-pub enum StepResult
-{
- /// Another step can be made.
- Continue,
-
- /// The world have been stopped so no step can be made again.
- Stop,
-}
-
-#[derive(Debug, Default)]
-struct WorldData
-{
- component_storage: ComponentStorage,
- sole_storage: SoleStorage,
- action_queue: Rc<ActionQueue>,
- new_events: Lock<NewEvents>,
-}
-
-#[derive(Debug, Clone)]
-pub struct EntityComponentRef<'a>
-{
- component_id: Uid,
- component: &'a ArchetypeEntityComponent,
- entity_id: Uid,
-}
-
-impl<'a> EntityComponentRef<'a>
-{
- fn component(&self) -> &'a Lock<Box<dyn Any>>
- {
- self.component.component()
- }
-
- #[must_use]
- pub fn id(&self) -> Uid
- {
- self.component_id
- }
-
- #[must_use]
- pub fn entity_id(&self) -> Uid
- {
- self.entity_id
- }
-
- fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid)
- -> Self
- {
- Self {
- component_id,
- component: comp,
- entity_id,
- }
- }
-}
-
-#[derive(Debug, Default)]
-struct ActionQueue
-{
- queue: Lock<Vec<Action>>,
-}
-
-impl ActionQueue
-{
- fn push(&self, action: Action)
- {
- self.queue
- .write_nonblock()
- .expect("Failed to aquire read-write lock to action queue")
- .push(action);
- }
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error("Sole {0} already exists")]
-pub struct SoleAlreadyExistsError(pub &'static str);
-
-#[derive(Debug)]
-struct StoredSole
-{
- sole: Arc<Lock<Box<dyn Sole>>>,
- drop_last: bool,
-}
-
-#[derive(Debug, Default)]
-struct SoleStorage
-{
- storage: HashMap<TypeId, ManuallyDrop<StoredSole>>,
-}
-
-impl SoleStorage
-{
- fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>>
- {
- self.storage
- .get(&TypeId::of::<SoleT>())
- .map(|sole| &sole.sole)
- }
-
- fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
- {
- let sole_type_id = TypeId::of::<SoleT>();
-
- if self.storage.contains_key(&sole_type_id) {
- return Err(SoleAlreadyExistsError(type_name::<SoleT>()));
- }
-
- let drop_last = sole.drop_last();
-
- // TODO: Reconsider this maybe?
- #[allow(clippy::arc_with_non_send_sync)]
- self.storage.insert(
- sole_type_id,
- ManuallyDrop::new(StoredSole {
- sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())),
- drop_last,
- }),
- );
-
- Ok(())
- }
-}
-
-impl Drop for SoleStorage
-{
- fn drop(&mut self)
- {
- let mut soles_to_drop_last = Vec::new();
-
- for sole in self.storage.values_mut() {
- if sole.drop_last {
- soles_to_drop_last.push(sole);
- continue;
- }
-
- unsafe {
- ManuallyDrop::drop(sole);
- }
- }
-
- for sole in &mut soles_to_drop_last {
- unsafe {
- ManuallyDrop::drop(sole);
- }
- }
- }
-}