concinnity_core/ecs/system.rs
1// src/ecs/system.rs
2//
3// The runtime behavior trait every engine system implements, plus its per-step
4// control signal. Renderer-free: `System` names only `PipelineContext` (which is
5// core), so it lives here where the physics / audio subsystem crates can name it
6// without depending on the renderer. The client `ecs` module re-exports both
7// under the historical `crate::ecs::*` paths, and its `define_systems!` table
8// names each system's gate; a world holds the built systems as trait objects.
9
10use crate::ecs::{Access, PipelineContext};
11
12/// What a system asks the world to do after its step.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum StepResult {
15 /// Keep running.
16 Continue,
17 /// This system is finished -- remove it from the active set.
18 /// The world exits naturally when no systems remain.
19 Done,
20 /// Hard stop -- halt everything immediately.
21 Stop,
22}
23
24/// System -- has behavior, receives a PipelineContext each tick. Every system
25/// is internal engine code: `World::start` constructs it from world components
26/// (via the system's own `new(..)`), so a system is never loaded from or
27/// written to a blob. `init` runs once at `World::start`; `step` runs every
28/// tick.
29///
30/// A world holds its systems as `dyn System`, so the trait is object-safe.
31/// `Send` is what lets a built world move to the simulation thread, and `Any`
32/// is what lets a caller holding the world reach one system as its own type
33/// (the `cn debug` / `cn editor` hot-reload drive).
34pub trait System: core::any::Any + core::fmt::Debug + Send {
35 /// Run once at `World::start`, before the first step.
36 fn init(&mut self, _ctx: &mut PipelineContext) {}
37
38 /// Run once per tick.
39 fn step(&mut self, ctx: &mut PipelineContext) -> StepResult;
40
41 /// The data `step` may touch, consulted once when the schedule is built
42 /// (after `init`, so a data-dependent system can compute it from its
43 /// compiled state). The default claims everything: an undeclared system is
44 /// ordered against all others and never runs concurrently, which is always
45 /// safe. Declaring narrower access is what admits a system to shared waves
46 /// and to debug-build access validation.
47 fn access(&self) -> Access {
48 Access::new().exclusive()
49 }
50}