How the hot_reloadable_module Facilitates Dynamic Code Updates in Equilibrium Engine
The hot_reloadable_module component stores module metadata and plugin context, enabling the Equilibrium Engine to detect file changes via cr.h, unload stale ECS entities, and re-import updated shared libraries without restarting the application.
The Equilibrium Engine is an open-source ECS (Entity Component System) framework that leverages the hot_reloadable_module to achieve runtime code modification. By treating hot-reloadable systems as ECS entities, the engine can dynamically swap implementation code while preserving application state, dramatically accelerating iteration cycles during development.
What Is the hot_reloadable_module?
The hot_reloadable_module is a specialized ECS component defined in equilibrium/components/hot_reloadable_module.h that bridges the engine’s entity system with the underlying cr.h hot-reloader library. It encapsulates two critical pieces of data:
module_name– A string identifier for the shared library (e.g.,"PhysicsSystem").plugin_ctx– A pointer to thecr_plugincontext managed bycr.h, which tracks the DLL/SO handle and version state.
By storing this metadata as a component, the engine can query all hot-reloadable modules using ECS filters, enabling bulk cleanup when a file change is detected.
How Dynamic Code Updates Work
The hot-reload lifecycle follows a strict registration-detection-cleanup-reimport pattern orchestrated by three core mechanisms.
The Registration Phase (ECS_IMPORT_HOT_RELOADABLE)
When the host application starts (or after a reload), it invokes the ECS_IMPORT_HOT_RELOADABLE(ctx, module) macro. This macro, defined in equilibrium/components/hot_reloadable_module.h (lines 81-95), performs three operations:
- Resolves the module’s ECS import path.
- Calls
ECS_IMPORTto load the module’s symbols into the world. - Creates an ECS entity tagged with the
HotReloadableModulecomponent, storing the currentcr_pluginpointer (ctx) and module name.
This registration links the logical module entity to the physical shared library file tracked by cr.h.
Detecting File Changes (cr.h Integration)
The Equilibrium Engine relies on cr.h (located at 3rdparty/cr/cr.h) to monitor shared library timestamps. In the host’s main loop, cr_plugin_update is called continuously. When the underlying DLL or SO file is modified on disk, cr.h returns the CR_LOAD operation code.
The engine detects this via the CHECK_IF_PLUGIN_RELOADED(ctx, operation) macro. When operation == CR_LOAD, the macro invokes reload_modules(ctx), triggering the cleanup phase.
The Cleanup and Re-import Cycle (reload_modules)
The reload_modules function (implemented in equilibrium/components/hot_reloadable_module.c) queries the ECS for all entities possessing a HotReloadableModule component whose plugin_ctx matches the reloaded cr_plugin pointer. For each match:
- It deletes the module’s entity using
ecs_delete, which cascades to remove all associated components and systems. - It clears the stale
cr_plugincontext, ensuring no dangling pointers remain.
After cleanup completes, the host application calls import_hot_reloadable_systems(ctx) again (as seen in launcher/sandbox/main.c), which re-executes ECS_IMPORT_HOT_RELOADABLE for each module. This loads the newly compiled shared library, registers fresh systems, and creates new HotReloadableModule entities bound to the updated code.
Implementation Example
The following pattern from launcher/sandbox/main.c demonstrates the complete integration:
#include "cr.h"
#include "equilibrium/components/hot_reloadable_module.h"
static void import_hot_reloadable_systems(struct cr_plugin *ctx) {
ECS_IMPORT_HOT_RELOADABLE(ctx, BootstrapSystem);
}
CR_EXPORT int cr_main(struct cr_plugin *ctx, enum cr_op operation) {
if (operation == CR_CLOSE) return 0;
static bool initialized = false;
if (!initialized) {
ECS_IMPORT(world, TransformSystem);
import_hot_reloadable_systems(ctx);
initialized = true;
} else {
CHECK_IF_PLUGIN_RELOADED(ctx, operation);
import_hot_reloadable_systems(ctx);
}
return 0;
}
In this example, BootstrapSystem resides in a separate shared library. When the developer recompiles BootstrapSystem.dll (or .so), the running application detects the change, invokes reload_modules to purge the old system, and re-imports the new binary without losing the world state managed by TransformSystem.
Key Files and Architecture
| File | Role |
|---|---|
equilibrium/components/hot_reloadable_module.h |
Defines HotReloadableModule component, ECS_IMPORT_HOT_RELOADABLE macro, and CHECK_IF_PLUGIN_RELOADED macro |
equilibrium/components/hot_reloadable_module.c |
Implements reload_modules function and component registration |
launcher/sandbox/main.c |
Example host application demonstrating import and reload detection |
editor/editor.c |
Advanced host with multiple hot-reloadable modules |
3rdparty/cr/cr.h |
Underlying C hot-reloader library providing cr_plugin context and CR_LOAD detection |
Summary
- The
hot_reloadable_moduleis an ECS component that stores module metadata and a pointer to thecr_plugincontext, enabling the engine to track which entities belong to a specific shared library. - Dynamic code updates are facilitated by the
ECS_IMPORT_HOT_RELOADABLEmacro, which registers modules, and thereload_modulesfunction, which cleans up stale entities whencr.hdetects a file change. - The system uses a detection-cleanup-reimport cycle:
CHECK_IF_PLUGIN_RELOADEDtriggers onCR_LOAD,reload_modulespurges old symbols viaecs_delete, and the host re-imports the new binary. - Because hot-reloadable modules are regular ECS entities, the engine preserves world state and non-reloadable systems (like
TransformSystem) while swapping only the targeted code.
Frequently Asked Questions
What triggers a dynamic code update in Equilibrium Engine?
A dynamic code update triggers when the cr_plugin_update function in 3rdparty/cr/cr.h detects that the timestamp or file size of the monitored shared library (DLL or SO) has changed. When this occurs, cr.h returns the CR_LOAD operation code to the host’s cr_main function, which the CHECK_IF_PLUGIN_RELOADED macro intercepts to initiate the reload sequence.
How does the engine prevent memory leaks during hot reloading?
The engine prevents memory leaks through the reload_modules function implemented in equilibrium/components/hot_reloadable_module.c. This function queries all ECS entities containing a HotReloadableModule component matching the reloaded plugin context and calls ecs_delete on each one. This deletion cascades through the ECS, automatically freeing all associated components, systems, and resources before the new module binary is imported.
Can multiple modules be hot-reloaded simultaneously?
Yes, multiple modules can be hot-reloaded simultaneously because each is tracked as a distinct ECS entity with its own HotReloadableModule component. The reload_modules function filters entities by the specific cr_plugin pointer passed to it, allowing the host to manage separate plugin contexts (e.g., PhysicsSystem and RenderingSystem as separate DLLs). When a file change is detected in any monitored library, only the entities associated with that specific plugin context are cleaned up and re-imported.
What is the role of cr.h in the hot_reloadable_module system?
The cr.h library (located at 3rdparty/cr/cr.h) serves as the underlying C hot-reloader that manages the shared library lifecycle. It provides the cr_plugin structure for context tracking, the cr_plugin_update function for polling file changes, and the CR_LOAD operation code that signals a reload event. The hot_reloadable_module system acts as an ECS abstraction layer over cr.h, converting low-level plugin events into entity lifecycle operations that integrate with the engine’s component system.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →