summaryrefslogtreecommitdiff
path: root/engine-ecs/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/lib.rs')
-rw-r--r--engine-ecs/src/lib.rs1033
1 files changed, 1033 insertions, 0 deletions
diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs
new file mode 100644
index 0000000..f9e8143
--- /dev/null
+++ b/engine-ecs/src/lib.rs
@@ -0,0 +1,1033 @@
+#![deny(clippy::all, clippy::pedantic)]
+#![allow(clippy::needless_pass_by_value, clippy::type_complexity)]
+
+use std::any::Any;
+use std::borrow::Cow;
+use std::fmt::Debug;
+use std::hint::cold_path;
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::Duration;
+
+use crate::actions::Action;
+use crate::bundle::Bundle;
+use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent;
+use crate::component::storage::{
+ EntityAlreadyExistsError,
+ Error as ComponentStorageError,
+ Storage as ComponentStorage,
+};
+use crate::component::{
+ Component,
+ Info as ComponentInfo,
+ IntoParts as IntoComponentParts,
+ Parts as ComponentParts,
+ WithAssociatedId,
+};
+use crate::entity::{
+ Declaration as EntityDeclaration,
+ Handle as EntityHandle,
+ Name as EntityName,
+};
+use crate::error::{
+ err_handler_panic,
+ HandlerFn as ErrorHandlerFn,
+ Metadata as ErrorMetadata,
+ SourceKind as ErrorSourceKind,
+};
+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,
+ Phase,
+ INIT as INIT_PHASE,
+ POST_UPDATE as POST_UPDATE_PHASE,
+ PRE_UPDATE as PRE_UPDATE_PHASE,
+ START as START_PHASE,
+ UPDATE as UPDATE_PHASE,
+};
+use crate::query::flexible::Query as FlexibleQuery;
+use crate::query::{
+ TermTuple as QueryTermTuple,
+ Terms as QueryTerms,
+ TermsBuilderInterface,
+ MAX_TERM_CNT as QUERY_MAX_TERM_CNT,
+};
+use crate::sole::{Single, Sole};
+use crate::system::observer::Observer;
+use crate::system::{
+ Callbacks,
+ Function as SystemFunction,
+ Metadata as SystemMetadata,
+ Param as SystemParam,
+ System,
+};
+use crate::uid::Uid;
+
+pub mod actions;
+pub mod bundle;
+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 system;
+pub mod time;
+pub mod tuple;
+pub mod uid;
+pub mod util;
+
+mod lock;
+
+pub use engine_ecs_macros::{Component, Sole};
+
+pub use crate::query::Query;
+
+pub extern crate engine_reflection as reflection;
+
+#[derive(Debug)]
+pub struct World
+{
+ data: WorldData,
+ stop: AtomicBool,
+ is_first_tick: AtomicBool,
+ error_handler: ErrorHandlerFn,
+}
+
+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,
+ };
+
+ let _ = world.add_sole(time::Time {
+ delta_time: Duration::default(),
+ tick_begin: None,
+ });
+
+ crate::phase::spawn_standard_phases(&mut world);
+
+ #[cfg(debug_assertions)]
+ world.register_system(*INIT_PHASE, crate::phase::check_parents);
+
+ crate::error::set_panic_hook();
+
+ world
+ }
+
+ pub fn set_err_handler(&mut self, err_handler: ErrorHandlerFn)
+ {
+ self.error_handler = err_handler;
+ }
+
+ pub fn stop_after_tick(&self)
+ {
+ self.stop.store(true, Ordering::Relaxed);
+ }
+
+ /// Creates a entity with the given components. A new unique [`Uid`] will be generated
+ /// for this entity.
+ pub fn spawn<BundleT: Bundle>(&mut self, bundle: BundleT) -> Uid
+ {
+ let entity_uid = Uid::new_unique();
+
+ self.spawn_with_uid(entity_uid, bundle);
+
+ entity_uid
+ }
+
+ /// Creates a entity with the given components. A new unique [`Uid`] will be generated
+ /// for this entity.
+ ///
+ /// In addition to the given components, the entity will have a [`EntityName`]
+ /// component containing `name`.
+ pub fn spawn_named<BundleT: Bundle>(
+ &mut self,
+ name: impl Into<Cow<'static, str>>,
+ bundle: BundleT,
+ ) -> Uid
+ {
+ let entity_uid = Uid::new_unique();
+
+ self.spawn_named_with_uid(entity_uid, name, bundle);
+
+ entity_uid
+ }
+
+ /// Creates a entity with the given components. The entity will have the specified
+ /// [`Uid`].
+ #[tracing::instrument(skip_all)]
+ pub fn spawn_with_uid<BundleT: Bundle>(&mut self, entity_uid: Uid, bundle: BundleT)
+ {
+ let components_parts = bundle.iter_component_parts();
+
+ self.create_ent(
+ entity_uid,
+ components_parts,
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+
+ /// Creates a entity with the given components. The entity will have the specified
+ /// [`Uid`].
+ ///
+ /// In addition to the given components, the entity will have a [`EntityName`]
+ /// component containing `name`.
+ #[tracing::instrument(skip_all)]
+ pub fn spawn_named_with_uid<BundleT: Bundle>(
+ &mut self,
+ entity_uid: Uid,
+ name: impl Into<Cow<'static, str>>,
+ bundle: BundleT,
+ )
+ {
+ let components_parts = bundle.iter_component_parts();
+
+ self.create_ent(
+ entity_uid,
+ components_parts.chain([EntityName { name: name.into() }.into_parts()]),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+
+ /// Adds a component to an entity.
+ ///
+ /// # Panics
+ /// This function will panic if the component is a [`Sole`].
+ pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts)
+ {
+ assert!(
+ !component_parts.is_sole,
+ "Cannot add sole component {} to an entity",
+ component_parts.name
+ );
+
+ Self::add_entity_components(
+ entity_id,
+ [component_parts],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+
+ pub fn spawn_declared_entity(&mut self, entity_decl: &EntityDeclaration)
+ {
+ entity_decl.spawn(self);
+ }
+
+ /// Adds a singleton component.
+ ///
+ /// # Errors
+ /// Returns `Err` if this [`Sole`] has already been added.
+ pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
+ where
+ SoleT: Sole,
+ {
+ if self
+ .data
+ .component_storage
+ .get_entity_archetype(SoleT::id())
+ .is_some()
+ {
+ return Err(SoleAlreadyExistsError);
+ }
+
+ let name = sole.name();
+
+ self.create_ent(
+ SoleT::id(),
+ [
+ sole.into_parts(),
+ EntityName { name: name.into() }.into_parts(),
+ crate::sole::Is.into_parts(),
+ ],
+ &ComponentAddingFlags { allow_soles: true },
+ );
+
+ Ok(())
+ }
+
+ pub fn register_observer<'this, SystemImpl, ObserverT>(
+ &'this mut self,
+ observer: ObserverT,
+ ) where
+ ObserverT: Observer<'this, SystemImpl>,
+ {
+ let (system, mut system_callbacks) = observer.finish_observer();
+
+ let ent_id = Uid::new_unique();
+
+ self.create_ent(
+ ent_id,
+ [SystemFunction { system }.into_parts()].into_iter().chain(
+ ObserverT::observed_events()
+ .into_iter()
+ .map(IntoComponentParts::into_parts),
+ ),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+
+ 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.spawn((SystemFunction { system: type_erased_system },));
+
+ system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });
+
+ self.spawn_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<'this, TermsT>(&'this self) -> Query<'this, TermsT>
+ where
+ TermsT: QueryTermTuple<'this>,
+ {
+ 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) -> Single<'_, SoleT>
+ {
+ Single::<SoleT>::new(self)
+ }
+
+ 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;
+ }
+
+ {
+ let mut time = self.get_sole::<time::Time>();
+
+ let Ok(time) = time.get_mut() else {
+ unreachable!();
+ };
+
+ time::update(&mut *time);
+ }
+
+ if self
+ .is_first_tick
+ .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ let Some(init_phase_entity) = self.get_entity(*INIT_PHASE) else {
+ unreachable!();
+ };
+
+ self.run_phase_systems(&init_phase_entity);
+
+ 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;
+ }
+
+ 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>,
+ flags: &ComponentAddingFlags,
+ )
+ {
+ debug_assert!(!entity_uid.is_pair());
+
+ 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),
+ flags,
+ );
+ }
+
+ 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::<SystemFunction>() 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() },
+ None,
+ )
+ } {
+ cold_path();
+
+ (self.error_handler)(
+ self,
+ 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()
+ .present([
+ 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(entity_id, components) => {
+ if let Err(err) = self.data.component_storage.create_entity(entity_id)
+ {
+ tracing::error!(
+ entity = %entity_id,
+ "Failed to create entity: {err}"
+ );
+
+ continue;
+ }
+
+ Self::add_entity_components(
+ entity_id,
+ components,
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+ Action::Despawn(entity_id) => {
+ if let Err(err) = self.data.component_storage.remove_entity(entity_id)
+ {
+ tracing::error!(
+ entity = %entity_id,
+ "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),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+ Action::RemoveComponents(entity_uid, component_ids) => {
+ Self::remove_entity_components(
+ entity_uid,
+ component_ids,
+ &mut self.data.component_storage,
+ );
+ }
+ Action::SetPair { entity_id, pair_id, new_pair } => {
+ if self.get_entity(entity_id).is_none() {
+ tracing::error!(
+ entity = %entity_id,
+ pair = %pair_id,
+ new_pair = %new_pair.id,
+ "Cannot set pair of a non-existant entity"
+ );
+ continue;
+ }
+
+ let _ = self
+ .data
+ .component_storage
+ .remove_entity_component(entity_id, pair_id);
+
+ Self::add_entity_components(
+ entity_id,
+ [new_pair],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ &ComponentAddingFlags { allow_soles: false },
+ );
+ }
+ Action::CreateSole(sole_parts) => {
+ if let Err(EntityAlreadyExistsError) =
+ self.data.component_storage.create_entity(sole_parts.id)
+ {
+ tracing::error!(
+ name = sole_parts.name,
+ "Can not create sole that already exists"
+ );
+ continue;
+ }
+
+ let name = sole_parts.name;
+
+ Self::add_entity_components(
+ sole_parts.id,
+ [
+ sole_parts,
+ EntityName { name: name.into() }.into_parts(),
+ crate::sole::Is.into_parts(),
+ ],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ &ComponentAddingFlags { allow_soles: true },
+ );
+ }
+ 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<'_>,
+ flags: &ComponentAddingFlags,
+ )
+ {
+ let component_iter = components.into_iter();
+
+ for component_parts in component_iter {
+ assert!(
+ flags.allow_soles || !component_parts.is_sole,
+ "Cannot add sole component '{}' to an entity",
+ component_parts.name
+ );
+
+ let comp_id = component_parts.id;
+ let comp_name = component_parts.name;
+ let comp_type_id = (*component_parts.data).type_id();
+
+ if let Err(err) = component_storage.add_entity_component(
+ entity_uid,
+ (comp_id, comp_name, component_parts.data),
+ ) {
+ let ent_name = Self::get_component_of_entity::<EntityName>(
+ component_storage,
+ entity_uid,
+ );
+
+ tracing::error!(
+ entity = %entity_uid,
+ entity_name = ent_name.as_ref().map(|ent_name| ent_name.name.as_ref()),
+ component = %comp_id,
+ component_name = comp_name,
+ "Failed to add component to entity: {err}"
+ );
+
+ continue;
+ }
+
+ if comp_id.is_pair() {
+ if let Some(pair_relation_metadata) =
+ &component_parts.pair_relation_metadata
+ {
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id.relation(),
+ ComponentInfo {
+ type_reflection: pair_relation_metadata.ty,
+ ty_id: pair_relation_metadata.ty_id,
+ name: pair_relation_metadata.name,
+ is_sole: pair_relation_metadata.is_sole,
+ },
+ );
+ }
+
+ if let Some(pair_target_metadata) = &component_parts.pair_target_metadata
+ {
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id.target(),
+ ComponentInfo {
+ type_reflection: pair_target_metadata.ty,
+ ty_id: pair_target_metadata.ty_id,
+ name: pair_target_metadata.name,
+ is_sole: pair_target_metadata.is_sole,
+ },
+ );
+ }
+
+ continue;
+ }
+
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id,
+ ComponentInfo {
+ type_reflection: component_parts.type_reflection,
+ ty_id: comp_type_id,
+ name: comp_name,
+ is_sole: component_parts.is_sole,
+ },
+ );
+
+ event_submitter.submit_event(
+ &Pair::builder()
+ .relation::<Added>()
+ .target_id(comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+ }
+
+ fn create_component_info_entity_if_missing(
+ component_storage: &mut ComponentStorage,
+ comp_id: Uid,
+ comp_info: ComponentInfo,
+ )
+ {
+ if let Some(comp_ent_archetype) = component_storage.get_entity_archetype(comp_id)
+ {
+ if comp_ent_archetype.contains_component_with_exact_id(ComponentInfo::id()) {
+ return;
+ }
+ } else if let Err(EntityAlreadyExistsError) =
+ component_storage.create_entity(comp_id)
+ {
+ unreachable!();
+ }
+
+ let comp_info_parts = comp_info.into_parts();
+
+ match component_storage.add_entity_component(
+ comp_id,
+ (
+ comp_info_parts.id,
+ comp_info_parts.name,
+ comp_info_parts.data,
+ ),
+ ) {
+ Ok(()) => {}
+ Err(ComponentStorageError::EntityDoesNotExist(_)) => {
+ // Entity is created if it does not exist
+ unreachable!();
+ }
+ Err(ComponentStorageError::ComponentAlreadyInEntity { .. }) => {
+ // This function returns early if ComponentInfo is already present
+ unreachable!();
+ }
+ Err(ComponentStorageError::ComponentNotFoundInEntity { .. }) => {
+ // This error is never returned by add_entity_component
+ unreachable!();
+ }
+ }
+ }
+
+ fn remove_entity_components(
+ entity_id: 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_id, component_id)
+ {
+ let ent_name = Self::get_component_of_entity::<EntityName>(
+ component_storage,
+ entity_id,
+ );
+
+ let component_info = Self::get_component_of_entity::<ComponentInfo>(
+ component_storage,
+ component_id,
+ );
+
+ tracing::error!(
+ entity = %entity_id,
+ entity_name = ent_name.as_ref().map(|ent_name| ent_name.name.as_ref()),
+ component = %component_id,
+ component_name = component_info
+ .as_ref()
+ .map(|component_info| component_info.name),
+ "Failed to remove component from entity: {err}"
+ );
+ }
+ }
+ }
+
+ pub fn get_component_of_entity<ComponentT: Component>(
+ component_storage: &ComponentStorage,
+ entity_id: Uid,
+ ) -> Option<crate::component::Handle<'_, ComponentT>>
+ {
+ let archetype = 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");
+ };
+
+ let comp_index = archetype.get_index_for_component(ComponentT::id())?;
+
+ let comp = &entity.components()[comp_index];
+
+ crate::component::Handle::from_entity_component_ref(&EntityComponentRef {
+ component_id: ComponentT::id(),
+ component: comp,
+ entity_id,
+ })
+ .ok()
+ }
+
+ fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>)
+ {
+ assert!(event_id.is_pair());
+
+ let query = Query::<(&SystemFunction,)>::from_flexible_query(
+ self.flexible_query(
+ QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
+ .present([SystemFunction::id(), event_id])
+ .build(),
+ ),
+ );
+
+ for (observer_ent_id, (observer,)) in query.iter_with_euids() {
+ if let Err(err) = unsafe {
+ observer.system.run(
+ self,
+ SystemMetadata { ent_id: observer_ent_id },
+ Some(emitted_event.clone()),
+ )
+ } {
+ cold_path();
+
+ (self.error_handler)(
+ self,
+ err,
+ &ErrorMetadata {
+ source_name: observer.system.name(),
+ source_kind: ErrorSourceKind::Observer,
+ },
+ );
+ }
+ }
+ }
+}
+
+impl Default for World
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
+}
+
+impl<'world> SystemParam<'world> for &'world World
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ world
+ }
+}
+
+/// 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,
+ 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 already exists")]
+pub struct SoleAlreadyExistsError;
+
+#[derive(Debug)]
+struct ComponentAddingFlags
+{
+ allow_soles: bool,
+}