Import Oxide engine (Stages 0–10) under MIT license

Full project snapshot migrated to new Gitea remote without history:
engine, editor, physics, script, examples, tests, docs, and assets.
Relicensed from GPLv3 to MIT and updated repo URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Homer Simpson
2026-07-05 20:41:02 +02:00
parent 2afb56b329
commit f56a1eea3b
128 changed files with 40493 additions and 2 deletions
+146
View File
@@ -0,0 +1,146 @@
//! The system schedule: the ordered phases an [`App`](super::App) runs each
//! frame, and the per-phase lists of systems modules attach to.
use super::App;
/// The ordered phases of one frame.
///
/// Systems are attached to a phase and run in phase order; within a phase they
/// run in registration order, so behavior is fully deterministic. The phases
/// mirror a conventional game loop:
///
/// - [`First`](Self::First) — start-of-frame bookkeeping.
/// - [`Input`](Self::Input) — gather input (Stage 7).
/// - [`PreUpdate`](Self::PreUpdate) — engine work before game logic.
/// - [`FixedUpdate`](Self::FixedUpdate) — fixed-timestep work; runs **zero or
/// more** times per frame so simulation is frame-rate independent. Physics
/// (Stage 9) lives here.
/// - [`Update`](Self::Update) — per-frame game logic.
/// - [`PostUpdate`](Self::PostUpdate) — engine work after game logic.
/// - [`Render`](Self::Render) — drawing (Stage 5 pipeline onward).
/// - [`Last`](Self::Last) — end-of-frame cleanup.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Schedule {
First,
Input,
PreUpdate,
FixedUpdate,
Update,
PostUpdate,
Render,
Last,
}
impl Schedule {
/// The once-per-frame phases, in order (everything except `FixedUpdate`,
/// which is driven separately by the fixed-timestep accumulator).
pub(crate) const PER_FRAME: [Schedule; 7] = [
Schedule::First,
Schedule::Input,
Schedule::PreUpdate,
Schedule::Update,
Schedule::PostUpdate,
Schedule::Render,
Schedule::Last,
];
}
/// One registered system: a closure plus the module that contributed it (so the
/// module can be disabled or removed).
pub(crate) struct SystemEntry {
pub(crate) module: Option<&'static str>,
pub(crate) run: Box<dyn FnMut(&mut App)>,
}
/// The collection of systems, grouped by phase, in registration order.
#[derive(Default)]
pub(crate) struct Systems {
first: Vec<SystemEntry>,
input: Vec<SystemEntry>,
pre_update: Vec<SystemEntry>,
fixed_update: Vec<SystemEntry>,
update: Vec<SystemEntry>,
post_update: Vec<SystemEntry>,
render: Vec<SystemEntry>,
last: Vec<SystemEntry>,
}
impl Systems {
fn phase_mut(&mut self, phase: Schedule) -> &mut Vec<SystemEntry> {
match phase {
Schedule::First => &mut self.first,
Schedule::Input => &mut self.input,
Schedule::PreUpdate => &mut self.pre_update,
Schedule::FixedUpdate => &mut self.fixed_update,
Schedule::Update => &mut self.update,
Schedule::PostUpdate => &mut self.post_update,
Schedule::Render => &mut self.render,
Schedule::Last => &mut self.last,
}
}
fn phase(&self, phase: Schedule) -> &[SystemEntry] {
match phase {
Schedule::First => &self.first,
Schedule::Input => &self.input,
Schedule::PreUpdate => &self.pre_update,
Schedule::FixedUpdate => &self.fixed_update,
Schedule::Update => &self.update,
Schedule::PostUpdate => &self.post_update,
Schedule::Render => &self.render,
Schedule::Last => &self.last,
}
}
pub(crate) fn push(&mut self, phase: Schedule, entry: SystemEntry) {
self.phase_mut(phase).push(entry);
}
pub(crate) fn total(&self) -> usize {
Schedule::PER_FRAME
.iter()
.chain(std::iter::once(&Schedule::FixedUpdate))
.map(|p| self.phase(*p).len())
.sum()
}
const ALL_PHASES: [Schedule; 8] = [
Schedule::First,
Schedule::Input,
Schedule::PreUpdate,
Schedule::FixedUpdate,
Schedule::Update,
Schedule::PostUpdate,
Schedule::Render,
Schedule::Last,
];
/// Drops every system contributed by `module`.
pub(crate) fn remove_module(&mut self, module: &str) {
for phase in Self::ALL_PHASES {
self.phase_mut(phase).retain(|e| e.module != Some(module));
}
}
/// Appends all of `other`'s systems (used to fold back systems registered
/// while the frame was running).
pub(crate) fn merge(&mut self, mut other: Systems) {
for phase in Self::ALL_PHASES {
let tail = std::mem::take(other.phase_mut(phase));
self.phase_mut(phase).extend(tail);
}
}
}
/// Runs one phase: every enabled system in registration order.
///
/// The [`Systems`] are moved out of the [`App`] before phases run (so systems
/// get exclusive `&mut App` access), so `systems` and `app` here are disjoint.
/// Systems from a disabled module are skipped without being removed.
pub(crate) fn run_phase(systems: &mut Systems, app: &mut App, phase: Schedule) {
for entry in systems.phase_mut(phase) {
if app.is_system_enabled(entry.module) {
(entry.run)(app);
}
}
}