Modules
Modules are import units. They let a library or feature register its components, relations, systems, and observers through one typed entry point.
Entities are not attached to modules. Component data is not removed when a module is disabled.
Declare A Module
Section titled “Declare A Module”Declare a module in a header:
ECS_MODULE_DECLARE(physics, { float gravity;});struct Physics { float gravity;
void import() { // Register components, systems, and observers here. }};This creates typed module properties named physics_props_t and declares the import
function:
void physics_import(const physics_props_t *props);// A module type exposes the same import entry point as a member function.void import();Define And Import
Section titled “Define And Import”Define the module in exactly one C file:
ECS_MODULE_DEFINE(physics);
void physics_import(const physics_props_t *props) { ECS_COMPONENT_REGISTER(world, Position); ECS_COMPONENT_REGISTER(world, Velocity);
ecs_system({ .name = "Move", .phase = EcsOnUpdate, .query = { .terms = { ecs_inout(Position), ecs_in(Velocity) }, }, .callback = move_system, });
(void)props;}struct Physics { float gravity;
void import() { ecs::system("Move").each([](Position &position, const Velocity &velocity) { position.x += velocity.x; }); }};Import it from application code:
ecs_module_id_t Physics = ECS_MODULE_IMPORT(world, physics, { .gravity = 9.81f,});auto physics = ecs::import<Physics>(9.81f);The props are passed only to physics_import(). Store any runtime settings
you need in components, globals owned by your module, or user-managed state.
Empty Modules
Section titled “Empty Modules”Modules with no parameters still use empty props:
ECS_MODULE_DECLARE(rendering, {});ECS_MODULE_DEFINE(rendering);
void rendering_import(const rendering_props_t *props) { (void)props; /* Register rendering systems and observers. */}
ECS_MODULE_IMPORT(world, rendering, {});struct Rendering { void import() { // Register rendering systems and observers. }};
auto rendering = ecs::import<Rendering>();What Gets Captured
Section titled “What Gets Captured”During ECS_MODULE_IMPORT(), SIECS records registrations made through the
normal public API:
ECS_COMPONENT_REGISTER(world, Position);ecs_system({ /* ... */ });ecs_observer({ /* ... */ });struct Physics { void import() { ecs::system("Move").each([](Position &, const Velocity &) {}); ecs::observe<ecs::OnSet>().each([](const Position &) {}); }};Those ids are appended to the active module. User code does not need a separate
module_add_* call.
Nested imports are supported. If one module imports another, registrations made after the nested import continue to belong to the parent module.
Enable And Disable
Section titled “Enable And Disable”Modules are enabled by default. Disabling a module disables only the systems and observers recorded by that module:
ecs_module_disable(Physics);ecs_module_enable(Physics);physics.disable();physics.enable();Components and relations stay registered. Existing component data stays valid. Component hooks still run because hooks belong to components, not to the module scheduler.
Import a module disabled with the generic API:
physics_props_t props = { .gravity = 9.81f };
ecs_module_id_t Physics = ecs_module({ .name = "physics", .id = &ecs_id(physics), .import = ecs_id(physics_import_wrapper), .desc = &props, .desc_size = sizeof(props), .disabled = true,});// Import first, then disable the captured systems and observers.auto physics = ecs::import<Physics>(9.81f);physics.disable();Idempotency
Section titled “Idempotency”Imports are idempotent for the active world. Importing the same module twice returns the same module id and does not register duplicate systems or observers:
ecs_module_id_t first = ECS_MODULE_IMPORT(world, physics, { .gravity = 9.81f });ecs_module_id_t second = ECS_MODULE_IMPORT(world, physics, { .gravity = 1.0f });
/* first == second */auto first = ecs::import<Physics>(9.81f);auto second = ecs::import<Physics>(1.0f);
assert(first.id() == second.id());The first props value wins. Later imports with different props values do not reconfigure an already imported module.
SIECS stores the imported id in the generated ecs_id(module_name) symbol and
resets it during ecs_fini(). A later world can import the module again, but the
same typed module is not supported in two live worlds at the same time.
Performance Model
Section titled “Performance Model”Modules do not add checks to query iteration, table matching, or system callbacks. Capture happens only during registration.
Enable and disable are linear over the ids stored in the module:
- systems are toggled with
ecs_system_enable()andecs_system_disable() - observers are toggled with
ecs_observer_enable()andecs_observer_disable() - components are retained for ownership/debug information and are not toggled
The runtime handle is ecs_module_id_t; no string lookup is needed to enable or
disable a module.