use std::{any::Any, collections::HashMap};
use zengine_ecs::{
system::{IntoSystem, System, SystemParam},
World,
};
pub use log;
pub trait Module {
fn init(self, engine: &mut Engine);
}
#[derive(Hash, Eq, PartialEq)]
pub enum Stage {
Startup,
PreUpdate,
Update,
PostUpdate,
PreRender,
Render,
PostRender,
}
#[derive(Default)]
struct SystemsStage {
systems: Vec<Box<dyn System>>,
}
impl SystemsStage {
pub fn init(&mut self, world: &mut World) {
for s in self.systems.iter_mut() {
s.init(world);
}
}
pub fn run(&mut self, world: &World) {
for s in self.systems.iter_mut() {
s.run(world);
}
}
pub fn apply(&mut self, world: &mut World) {
for s in self.systems.iter_mut() {
s.apply(world);
}
}
pub fn run_and_apply(&mut self, world: &mut World) {
for s in self.systems.iter_mut() {
s.run(world);
s.apply(world);
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum EngineEvent {
Quit,
Suspended,
Resumed,
}
pub struct Engine {
stages: HashMap<Stage, SystemsStage>,
stage_order: Vec<Stage>,
running_stages: Vec<SystemsStage>,
pub world: World,
runner: Box<dyn Fn(Engine)>,
}
impl Default for Engine {
fn default() -> Self {
Engine {
stages: HashMap::from([
(Stage::Startup, SystemsStage::default()),
(Stage::PreUpdate, SystemsStage::default()),
(Stage::Update, SystemsStage::default()),
(Stage::PostUpdate, SystemsStage::default()),
(Stage::Render, SystemsStage::default()),
(Stage::PostRender, SystemsStage::default()),
]),
stage_order: vec![
Stage::Startup,
Stage::PreUpdate,
Stage::Update,
Stage::PostUpdate,
Stage::Render,
Stage::PostRender,
],
running_stages: Vec::default(),
world: World::default(),
runner: Box::new(default_runner),
}
}
}
fn default_runner(mut engine: Engine) {
engine.startup();
loop {
engine.update();
if engine
.world
.get_event_handler::<EngineEvent>()
.and_then(|event| event.read_last().map(|e| e == &EngineEvent::Quit))
.unwrap_or(false)
{
break;
}
}
}
impl Engine {
pub fn init_logger(level: log::Level) {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(level).expect("Couldn't initialize logger");
} else {
use simplelog::{Config, SimpleLogger, TermLogger, TerminalMode};
let level_filter = level.to_level_filter();
if TermLogger::init(level_filter, Config::default(), TerminalMode::Mixed).is_err() {
SimpleLogger::init(level_filter, Config::default())
.expect("Couldn't initialize logger")
}
log_panics::init();
}
}
}
pub fn add_system<Params: SystemParam + Any, I: IntoSystem<Params> + Any>(
&mut self,
system: I,
) -> &mut Self {
self.add_system_into_stage(system, Stage::Update)
}
pub fn add_startup_system<Params: SystemParam + Any, I: IntoSystem<Params> + Any>(
&mut self,
system: I,
) -> &mut Self {
self.add_system_into_stage(system, Stage::Startup)
}
pub fn add_system_into_stage<Params: SystemParam + Any, I: IntoSystem<Params> + Any>(
&mut self,
system: I,
stage: Stage,
) -> &mut Self {
if let Some(stage) = self.stages.get_mut(&stage) {
stage.systems.push(Box::new(system.into_system()));
}
self
}
pub fn add_module(&mut self, module: impl Module) -> &mut Self {
module.init(self);
self
}
pub fn set_runner<F: Fn(Engine) + 'static>(&mut self, runner: F) -> &mut Self {
self.runner = Box::new(runner);
self
}
pub fn startup(&mut self) {
let mut stages: Vec<SystemsStage> = self
.stage_order
.iter()
.map(|stage| self.stages.remove(stage).unwrap())
.collect();
for stage in stages.iter_mut() {
stage.init(&mut self.world);
}
let mut startup_stage = stages.remove(0);
startup_stage.run_and_apply(&mut self.world);
self.running_stages = stages;
}
pub fn update(&mut self) {
for stage in self.running_stages.iter_mut() {
stage.run(&self.world);
}
for stage in self.running_stages.iter_mut() {
stage.apply(&mut self.world);
}
}
pub fn run(&mut self) {
self.world.create_event_handler::<EngineEvent>();
let mut app = std::mem::take(self);
let runner = std::mem::replace(&mut app.runner, Box::new(default_runner));
(runner)(app);
}
}