# App, Modules & Scheduling `oxide_engine::app` is where the Stage-5 core framework comes together. An [`App`] owns the shared engine state and a [`Schedule`] of systems; functionality is added by **modules**. This is the spine the rest of the engine plugs into: the engine is *composed* rather than hard-wired, and an exported game compiles in only the modules it registers. ## The App An `App` owns: - the active [`Scene`](scene.md), - the shared [`AssetServer`](assets.md), - the [`TypeRegistry`](reflection.md) (dual-editable components), - the project's [`LayerRegistry`](layers.md), - frame [`Time`], and - arbitrary user **resources** (a type-keyed store). ```rust use oxide_engine::app::{App, DefaultModules}; let mut app = App::new(); app.add_modules(DefaultModules); app.update(1.0 / 60.0); // advance one frame ``` > Note: the application core is `oxide_engine::app::App`. It is intentionally > *not* in the prelude, to avoid clashing with the windowing > [`App`](windowing.md) trait (the per-window event handler). Import it directly. ### Resources Resources are shared singletons addressed by type — the home for state that isn't per-entity (an input map, a physics world, game settings): ```rust # use oxide_engine::app::App; # let mut app = App::new(); app.insert_resource(0u32); *app.get_resource_mut::().unwrap() += 1; assert_eq!(app.get_resource::(), Some(&1)); ``` ## Systems and the schedule A **system** is any `FnMut(&mut App)` attached to a [`Schedule`] phase. Phases run in a fixed order each frame; within a phase, systems run in registration order, so behavior is fully deterministic. | Phase | Purpose | |-------|---------| | `First` | start-of-frame bookkeeping | | `Input` | gather input (Stage 7) | | `PreUpdate` | engine work before game logic | | `FixedUpdate` | fixed-timestep work; runs **0..n** times per frame (physics, Stage 9) | | `Update` | per-frame game logic | | `PostUpdate` | engine work after game logic | | `Render` | drawing (Stage 5 pipeline onward) | | `Last` | end-of-frame cleanup | ```rust use oxide_engine::app::{App, Schedule}; use oxide_engine::prelude::*; let mut app = App::new(); app.scene.spawn("spinner", Transform::IDENTITY); app.add_system(Schedule::Update, |app| { let dt = app.time.delta; for e in app.scene.entities().collect::>() { if let Some(mut t) = app.scene.get_mut::(e) { t.translation.x += dt; } } }); ``` Systems get exclusive `&mut App` while running (the schedule is moved out of the app for the duration), so a system can freely read and mutate the scene, resources, and assets. ### Fixed timestep `FixedUpdate` is driven by an accumulator so simulation is frame-rate independent: each `update(dt)` runs as many whole `fixed_delta` steps as the accumulated time allows. The number of steps per frame is **capped** so a long stall (a breakpoint, a hitch) cannot trigger an unbounded catch-up "spiral of death". Set the rate with `app.set_fixed_timestep(seconds)` (default 1/60). The whole frame's scheduling overhead is a few dozen nanoseconds even with several systems registered (see `cargo bench -p oxide-engine --bench app`), so it is lost in the noise next to any real per-frame work. ## Modules A [`Module`] is the unit of engine extension. Its [`build`](Module::build) method registers systems, component types, asset loaders, and resources through the `App` facade. Everything registered during `build` is **attributed to the module**, so it can be enabled, disabled, or removed as one unit. ```rust use oxide_engine::app::{App, Module, Schedule}; struct HeartbeatModule; impl Module for HeartbeatModule { fn name(&self) -> &'static str { "heartbeat" } fn build(&self, app: &mut App) { app.insert_resource(0u64); app.add_system(Schedule::Update, |app| { *app.get_resource_mut::().unwrap() += 1; }); } } let mut app = App::new(); app.add_module(HeartbeatModule); ``` ### Enable / disable / remove These power the editor's module management (Stage 6) and the "ship only what you use" principle: ```rust # use oxide_engine::app::{App, Module, Schedule}; # struct HeartbeatModule; # impl Module for HeartbeatModule { # fn name(&self) -> &'static str { "heartbeat" } # fn build(&self, app: &mut App) {} # } # let mut app = App::new(); # app.add_module(HeartbeatModule); app.set_module_enabled("heartbeat", false); // systems skipped, nothing removed app.set_module_enabled("heartbeat", true); // resumes app.remove_module("heartbeat"); // systems, types, loaders, resources gone ``` `remove_module` undoes every registration the module made — its systems, reflected types, asset loaders, and resources — leaving no dangling references. Disabling is the cheap, reversible version (systems are skipped but kept). ### Built-in modules `DefaultModules` bundles the engine's standard set: - **`core`** — registers the always-present scene component types (`Transform`, `Node`, `Layer`, `Tags`) for reflection, exposing them to the editor and scripts. - **`render`** — registers the renderable `MeshRenderer` component and (as the data-driven render pipeline grows) the render-phase systems. Subsystems from Stage 9 on (physics, audio, …) are built as their own feature-gated crates, each exposing a `Module`, so a project pays for them only by registering them. [`App`]: ../engine/src/app/mod.rs [`Time`]: ../engine/src/app/mod.rs [`Schedule`]: ../engine/src/app/schedule.rs [`Module`]: ../engine/src/app/module.rs [`Scene`]: ../engine/src/scene/graph.rs