#![feature(trait_alias)]
use newport_core::containers::{ Box, HashMap };
use newport_os::window::{ WindowBuilder, Window, WindowEvent };
use std::any::TypeId;
use std::sync::atomic::{ AtomicBool, Ordering };
use std::any::Any;
use std::time::Instant;
static mut ENGINE: Option<Engine> = None;
pub struct Engine {
name: String,
modules: HashMap<TypeId, Box<dyn ModuleRuntime>>,
is_running: AtomicBool,
window: Window,
}
impl Engine {
pub fn run(mut builder: EngineBuilder) -> Result<&'static Engine, ()> {
let mut modules = HashMap::with_capacity(builder.entries.len());
for it in builder.entries {
if modules.contains_key(&it.id) { continue; }
modules.insert(it.id, (it.spawn)());
}
let name;
if builder.name.is_some() {
name = builder.name.unwrap();
} else {
name = "project".to_string();
}
unsafe{
let window = WindowBuilder::new()
.title(name.clone())
.spawn()
.unwrap();
ENGINE = Some(Engine{
name: name,
modules: modules,
is_running: AtomicBool::new(true),
window: window,
});
let engine = ENGINE.as_mut().unwrap();
for (_, module) in engine.modules.iter_mut() {
module.post_init(ENGINE.as_mut().unwrap());
}
builder.post_inits.drain(..).for_each(|init| init(ENGINE.as_mut().unwrap()));
engine.modules.iter_mut().for_each(|(_, module)| module.on_startup());
engine.window.set_visible(true);
let mut _fps = 0;
let mut frame_count = 0;
let mut time = 0.0;
let mut last_frame_time = Instant::now();
'run: while engine.is_running.load(Ordering::Relaxed) {
let now = Instant::now();
let dt = now.duration_since(last_frame_time).as_secs_f32();
last_frame_time = now;
time += dt;
if time >= 1.0 {
time = 0.0;
_fps = frame_count;
frame_count = 0;
}
frame_count += 1;
for event in engine.window.poll_events() {
match event {
WindowEvent::Closed => {
engine.is_running.store(false, Ordering::Relaxed);
break 'run;
}
_ => { }
}
}
for (_, v) in ENGINE.as_ref().unwrap().modules.iter() {
v.on_tick(dt);
}
}
}
Ok(Self::as_ref())
}
pub fn as_ref() -> &'static Engine {
unsafe{ ENGINE.as_ref().unwrap() }
}
pub fn module<'a, T: Module>(&'a self) -> Option<&'a T> {
let id = TypeId::of::<T>();
let module = self.modules.get(&id)?;
module.as_any().downcast_ref::<T>()
}
pub fn module_mut<'a, T: Module>(&'a mut self) -> Option<&'a mut T> {
let id = TypeId::of::<T>();
let module = self.modules.get_mut(&id)?;
module.as_any_mut().downcast_mut::<T>()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn window(&self) -> &Window {
&self.window
}
}
struct EngineBuilderEntry {
id: TypeId,
spawn: fn() -> Box<dyn ModuleRuntime>,
}
pub struct EngineBuilder {
entries: Vec<EngineBuilderEntry>,
name: Option<String>,
post_inits: Vec<Box<dyn FnOnce(&'static mut Engine) + 'static>>,
}
impl EngineBuilder {
pub fn new() -> Self {
Self {
entries: Vec::with_capacity(32),
name: None,
post_inits: Vec::new(),
}
}
pub fn module<T: Module>(mut self) -> Self {
fn spawn<T: Module>() -> Box<dyn ModuleRuntime> {
Box::new(T::new())
}
self = T::depends_on(self);
self.entries.push(EngineBuilderEntry{
id: TypeId::of::<T>(),
spawn: spawn::<T>,
});
self
}
pub fn post_init<F: FnOnce(&'static mut Engine) + 'static>(mut self, f: F) -> Self {
self.post_inits.push(Box::new(f));
self
}
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
}
pub trait ModuleCompileTime: Sized + 'static {
fn new() -> Self;
fn depends_on(builder: EngineBuilder) -> EngineBuilder {
builder
}
}
pub trait ModuleRuntime: AsAny {
fn post_init(&mut self, _: &mut Engine) { }
fn on_startup(&'static mut self) { }
fn on_tick(&self, _dt: f32) { }
}
pub trait Module = ModuleRuntime + ModuleCompileTime;
pub trait AsAny {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: ModuleRuntime + 'static> AsAny for T {
fn as_any(&self) -> &dyn Any{
self
}
fn as_any_mut(&mut self) -> &mut dyn Any{
self
}
}