summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-05-02 18:47:18 +0200
committerHampusM <hampus@hampusmat.com>2026-05-02 18:47:18 +0200
commit9dfd0a9ef9a54dab5fb88ed9158bd4184212008e (patch)
treee40cbfb018c487d78ef1c570258a87a1d2245e98
parente698922e86c217a261db114ce0392060503f0013 (diff)
fix(ecs): make system with local comps not low/high priority in run order
-rw-r--r--ecs/src/lib.rs68
-rw-r--r--ecs/src/pair.rs4
-rw-r--r--ecs/src/phase.rs5
-rw-r--r--ecs/tests/phase.rs36
4 files changed, 81 insertions, 32 deletions
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index 7a2178e..32ade13 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -2,6 +2,7 @@
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;
@@ -23,8 +24,9 @@ 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, DependsOn, Pair};
+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,
@@ -171,15 +173,18 @@ impl World
{
let (type_erased_system, mut system_callbacks) = system.finish();
- let system_ent_id = self.create_entity((
- SystemComponent { system: type_erased_system },
- Pair::builder()
- .relation::<DependsOn>()
- .target_id(phase_euid)
- .build(),
- ));
+ 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.
@@ -246,7 +251,11 @@ impl World
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
- self.query_and_run_systems(*START_PHASE);
+ let Some(start_phase_entity) = self.get_entity(*START_PHASE) else {
+ unreachable!();
+ };
+
+ self.run_phase_systems(&start_phase_entity);
}
self.perform_phases();
@@ -346,29 +355,26 @@ impl World
);
}
- fn query_and_run_systems(&self, phase_euid: Uid)
+ fn run_phase_systems(&self, phase_entity: &EntityHandle<'_>)
{
- let system_query = Query::<(&SystemComponent,)>::from_flexible_query(
- self.flexible_query(
- QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
- .with_required([
- SystemComponent::id(),
- Pair::builder()
- .relation::<DependsOn>()
- .target_id(phase_euid)
- .build()
- .id(),
- ])
- .build(),
- ),
- );
+ // 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;
+ };
- for (system_ent_id, (system_component,)) in system_query.iter_with_euids() {
// SAFETY: The world lives long enough
unsafe {
- system_component
+ system
.system
- .run(self, SystemMetadata { ent_id: system_ent_id });
+ .run(self, SystemMetadata { ent_id: system_entity.uid() });
}
}
}
@@ -389,14 +395,18 @@ impl World
);
for child_phase_entity in &phase_query {
- self.query_and_run_systems(child_phase_entity.uid());
+ self.run_phase_systems(&child_phase_entity);
self.perform_child_phases(child_phase_entity.uid());
}
}
fn perform_single_phase(&self, phase_entity_id: Uid)
{
- self.query_and_run_systems(phase_entity_id);
+ 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);
}
diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs
index b4bfa57..0d353e3 100644
--- a/ecs/src/pair.rs
+++ b/ecs/src/pair.rs
@@ -460,13 +460,13 @@ impl<'world, Relation, Target> WithWildcard<'world, Relation, Target>
}
}
-impl<Relation> WithWildcard<'_, Relation, Wildcard>
+impl<'world, Relation> WithWildcard<'world, Relation, Wildcard>
where
Relation: Component,
{
/// Attempts to retrieve the target as a entity, returning `None` if not found.
#[must_use]
- pub fn get_target_ent(&self) -> Option<EntityHandle<'_>>
+ pub fn get_target_ent(&self) -> Option<EntityHandle<'world>>
{
let archetype = self
.world
diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs
index 39f2a08..e8d9b71 100644
--- a/ecs/src/phase.rs
+++ b/ecs/src/phase.rs
@@ -1,6 +1,6 @@
use ecs_macros::Component;
-use crate::{declare_entity, World};
+use crate::{World, declare_entity};
#[derive(Debug, Default, Clone, Copy, Component)]
pub struct Phase;
@@ -17,3 +17,6 @@ pub(crate) fn spawn_entities(world: &mut World)
world.create_declared_entity(&UPDATE);
world.create_declared_entity(&POST_UPDATE);
}
+
+#[derive(Debug, Component)]
+pub(crate) struct HasSystem;
diff --git a/ecs/tests/phase.rs b/ecs/tests/phase.rs
new file mode 100644
index 0000000..af2646b
--- /dev/null
+++ b/ecs/tests/phase.rs
@@ -0,0 +1,36 @@
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+use ecs::component::local::Local;
+use ecs::phase::UPDATE;
+use ecs::system::Into;
+use ecs::system::initializable::Initializable;
+use ecs::{Component, World};
+
+#[derive(Component)]
+struct Thing;
+
+#[test]
+fn system_run_order_correct_when_one_has_local_comp()
+{
+ static COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+ fn first_system(_thing: Local<Thing>)
+ {
+ assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 0);
+ }
+
+ fn second_system()
+ {
+ assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 1);
+ }
+
+ let mut world = World::new();
+
+ world.register_system(*UPDATE, first_system.into_system().initialize((Thing,)));
+
+ world.register_system(*UPDATE, second_system);
+
+ world.step();
+
+ assert_eq!(COUNTER.load(Ordering::Relaxed), 2);
+}