Hot-reloadable Flecs modules as Rust dylibs. Covers module architecture, component vs system modules, and inter-module dependencies.
Hot-reloadable Flecs modules as Rust dylibs.
Use this skill when you need to:
In Flecs, it's idiomatic to separate:
This separation enables:
crates/module/
├── time-components/ # WorldTime, TpsTracker (components only)
├── time-systems/ # Systems that tick WorldTime (imports time-components)
├── network-components/ # Connection, PacketBuffer, etc.
├── network-systems/ # Ingress/egress systems
└── play/ # Game systems (imports time-components, network-components)
// crates/module/time-components/src/lib.rs
use flecs_ecs::prelude::*;
#[derive(Component, Debug)]
pub struct WorldTime {
pub world_age: i64,
pub time_of_day: i64,
}
#[derive(Component)]
pub struct TimeComponentsModule;
impl Module for TimeComponentsModule {
fn module(world: &World) {
world.module::<TimeComponentsModule>("time::components");
// Register component and singleton
world.component::<WorldTime>().add_trait::<flecs::Singleton>();
world.set(WorldTime::default());
// NO SYSTEMS HERE - just components
}
}
// crates/module/time-systems/src/lib.rs
use flecs_ecs::prelude::*;
use module_time_components::{TimeComponentsModule, WorldTime};
#[derive(Component)]
pub struct TimeSystemsModule;
impl Module for TimeSystemsModule {
fn module(world: &World) {
world.module::<TimeSystemsModule>("time::systems");
// Import component module (ensures components exist)
world.import::<TimeComponentsModule>();
// Define systems
world.system_named::<&mut WorldTime>("TickWorldTime")
.each(|time| {
time.world_age += 1;
time.time_of_day = (time.time_of_day + 1) % 24000;
});
}
}
System modules depend on component modules via Cargo:
# crates/module/time-systems/Cargo.toml
[lib]
crate-type = ["dylib"]
[dependencies]
flecs_ecs.workspace = true
module-time-components = { path = "../time-components" }
Each module dylib exports:
#[unsafe(no_mangle)]
pub fn module_load(world: &World) {
world.import::<MyModule>();
}
#[unsafe(no_mangle)]
pub fn module_unload(world: &World) {
if let Some(e) = world.try_lookup("::my_module") {
e.destruct();
}
}
#[unsafe(no_mangle)]
pub fn module_name() -> &'static str { "my_module" }
#[unsafe(no_mangle)]
pub fn module_version() -> u32 { 1 }
// WRONG - just registers component
world.component::<WorldTime>();
// RIGHT - register as singleton AND set value
world.component::<WorldTime>().add_trait::<flecs::Singleton>();
world.set(WorldTime::default());
world.import::<Module>() is idempotent - safe to call multiple times:
impl Module for PlayModule {
fn module(world: &World) {
world.import::<TimeComponentsModule>(); // Ensures components exist
world.import::<NetworkComponentsModule>();
// Now safe to query WorldTime, PacketBuffer, etc.
}
}
All modules must link to the SAME shared flecs libraries:
libflecs.dylib (C library)libflecs_ecs.dylib (Rust wrapper)Symlink dylibs to modules/ directory:
ln -s target/debug/libmodule_time_components.dylib modules/
ln -s target/debug/libmodule_time_systems.dylib modules/
Rebuild updates dylib, file watcher triggers hot-reload.
With component/system separation:
time-systems → systems restart with existing component datatime-components → resets singletons (use sparingly)npx skills add andrewgazelka/flecs-dylib-modules下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer