Frame Manager
FrameManager is the runtime orchestrator that drives the FramePhase
lifecycle and dispatches registered ForgeService implementations on each
tick. It now lives in plant_runner and is imported into plant_world
via a thin shim.
For background on frame phases and service dispatch ordering, see Frame Lifecycle.
FrameManager
pub struct FrameManager { /* async inner state */ }
FrameManager is the primary entry point for hosts. It manages:
- The current
FramePhaseandFrameState - The ordered set of registered services (
RegisteredService) - The
HookDispatcherfor string-keyed lifecycle callbacks - Frame history and dust accounting
Key Methods
impl FrameManager {
pub fn new(config: FrameLifecycleConfig) -> Self;
/// Register a ForgeService implementation. Services are dispatched
/// in ascending priority order within each phase.
pub async fn register_service(
&mut self,
service: Box<dyn ForgeService>,
) -> Result<()>;
/// Advance one full frame (BeginningFrame -> ... -> Idle).
pub async fn tick(&mut self) -> Result<FrameId>;
/// Describe all currently registered services (for diagnostics).
pub fn service_descriptors(&self) -> Vec<ServiceDescriptor>;
}
Construction
let config = FrameLifecycleConfig::default();
let mut fm = FrameManager::new(config);
// Register services before the first tick
fm.register_service(Box::new(HobService::new())).await?;
fm.register_service(Box::new(region_forge_service)).await?;
RegisteredService
pub struct RegisteredService {
pub descriptor: ServiceDescriptor,
pub service: Box<dyn ForgeService>,
}
Holds both the descriptor (for inspection) and the live service (for dispatch).
RegisteredService records are kept sorted by priority.
ServiceDescriptor
#[derive(Debug, Clone, Serialize)]
pub struct ServiceDescriptor {
pub kind: String,
pub priority: u32,
pub frame_budget: u32,
pub active_interval_frames: u64,
}
Returned by FrameManager::service_descriptors() for external health checks
and diagnostics. The kind field equals the service's service_name() return
value.
active_interval_frames controls how often on_frame_active fires for that
service. A value of 1 means every frame; 10 means every 10th frame. Other
hooks (on_frame_init, on_frame_aggregation, on_frame_cleanup) always fire
every frame regardless of this setting.
The default is DEFAULT_SERVICE_INTERVAL:
pub const DEFAULT_SERVICE_INTERVAL: u64 = 1;
BrandPatchSink
pub trait BrandPatchSink: Send + Sync {
fn enqueue_input_patch(&self, hob_id: String, payload: String) -> Result<(), String>;
}
BrandPatchSink decouples FrameManager from the brand plugin feature. The
manager holds an optional Arc<dyn BrandPatchSink> and calls it when brand
commands are received, without taking a direct dependency on wasmtime or any
WIT bindings.
Implementations live in plant_world::brand_plugin and are injected into the
manager during host startup:
frame_manager.set_patch_sink(Arc::new(my_brand_host));
Relationship to plant_world
plant_world imports FrameManager via a shim:
// world/src/frame_manager.rs
pub use plant_runner::frame_manager::*;
Call sites in plant_world that use FrameManager, RegisteredService,
ServiceDescriptor, BrandPatchSink, or DEFAULT_SERVICE_INTERVAL compile
against plant_runner types transparently.