#[cfg(test)] #[macro_use]
extern crate assert_matches;
extern crate nalgebra_glm as glm;
#[cfg(test)]
extern crate self as gtether;
use educe::Educe;
use parking_lot::RwLock;
use std::any::Any;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::ops::Deref;
use std::sync::Arc;
use std::thread::JoinHandle;
use tracing::debug;
use crate::app::driver::AppDriver;
use crate::app::Application;
use crate::event::{EventBus, EventBusRegistry};
use crate::net::driver::NetDriverFactory;
use crate::net::Networking;
use crate::resource::manager::ResourceManager;
pub mod app;
pub mod console;
pub mod event;
#[cfg(feature = "gui")]
pub mod gui;
pub mod net;
#[cfg(feature = "graphics")]
pub mod render;
pub mod resource;
pub mod util;
pub mod worker;
#[derive(Debug, Clone)]
pub struct InvalidEngineStageTransition {
pub current: EngineStage,
pub next: EngineStage,
}
impl Display for InvalidEngineStageTransition {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Invalid transition from stage '{:?}' to stage '{:?}", &self.current, &self.next)
}
}
impl Error for InvalidEngineStageTransition {}
#[cfg_attr(doc, aquamarine::aquamarine)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EngineStage {
Init,
Running,
Resuming,
Suspending,
Suspended,
Stopping,
Stopped,
}
impl Default for EngineStage {
#[inline]
fn default() -> Self {
Self::Init
}
}
impl EngineStage {
pub fn validate_transition(&self, next: EngineStage)
-> Result<(), InvalidEngineStageTransition> {
if self == &next {
return Ok(())
}
match (self, next) {
(Self::Init, Self::Running) => Ok(()),
(Self::Running, Self::Suspending) => Ok(()),
(Self::Suspending, Self::Suspended) => Ok(()),
(Self::Suspended, Self::Resuming) => Ok(()),
(Self::Resuming, Self::Running) => Ok(()),
(Self::Running | Self::Suspended, Self::Stopping) => Ok(()),
(Self::Stopping, Self::Stopped) => Ok(()),
_ => Err(InvalidEngineStageTransition { current: *self, next })
}
}
}
#[derive(Debug)]
pub struct EngineStageChangedEvent {
previous: EngineStage,
current: EngineStage,
}
impl EngineStageChangedEvent {
#[inline]
pub fn previous(&self) -> EngineStage { self.previous }
#[inline]
pub fn current(&self) -> EngineStage { self.current }
}
#[derive(Educe)]
#[educe(Debug)]
struct EngineStateInner {
stage: EngineStage,
#[educe(Debug(ignore))]
event_bus: Arc<EventBus>,
}
impl EngineStateInner {
fn new(event_bus: Arc<EventBus>) -> Self {
Self {
stage: EngineStage::default(),
event_bus,
}
}
fn transition_stage(&mut self, stage: EngineStage) -> Result<(), InvalidEngineStageTransition> {
if stage != self.stage {
self.stage.validate_transition(stage)?;
let previous = self.stage;
debug!(?previous, current = ?stage, "Engine transitioning stage");
self.stage = stage;
self.event_bus.fire(EngineStageChangedEvent {
previous,
current: stage,
});
}
Ok(())
}
}
pub struct EngineState<A: Application> {
engine: Arc<Engine<A>>,
}
impl<A: Application> EngineState<A> {
pub fn set_stage(&self, stage: EngineStage) -> Result<(), InvalidEngineStageTransition> {
self.engine.state.write().transition_stage(stage)
}
}
impl<A: Application> Deref for EngineState<A> {
type Target = Arc<Engine<A>>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.engine
}
}
pub struct Engine<A: Application> {
app: A,
app_driver: A::ApplicationDriver,
state: Arc<RwLock<EngineStateInner>>,
event_bus: Arc<EventBus>,
resources: Arc<ResourceManager>,
networking: Arc<Networking<A::NetworkingDriver>>,
}
impl<A: Application> Engine<A> {
#[inline]
pub fn builder() -> EngineBuilder<A> {
EngineBuilder::new()
}
#[inline]
pub fn app(&self) -> &A { &self.app }
#[inline]
pub fn stage(&self) -> EngineStage {
self.state.read().stage
}
#[inline]
pub fn event_bus(&self) -> &EventBusRegistry {
self.event_bus.registry()
}
#[inline]
pub fn resources(&self) -> &Arc<ResourceManager> { &self.resources }
#[inline]
pub fn net(&self) -> &Arc<Networking<A::NetworkingDriver>> {
&self.networking
}
pub fn start(self: &Arc<Self>) {
let engine_state = EngineState {
engine: self.clone(),
};
self.app_driver.run_main_loop(engine_state);
}
pub fn stop(&self) -> Result<(), InvalidEngineStageTransition> {
let mut state = self.state.write();
if state.stage == EngineStage::Stopped {
Ok(())
} else {
state.transition_stage(EngineStage::Stopping)
}
}
}
#[cfg(feature = "graphics")]
impl<A> Engine<A>
where
A: Application,
A::ApplicationDriver: render::AppDriverGraphicsVulkan,
{
#[inline]
pub fn render_instance(&self) -> Arc<render::Instance> {
use render::AppDriverGraphicsVulkan;
self.app_driver.render_instance()
}
}
#[cfg(feature = "gui")]
impl<A> Engine<A>
where
A: Application,
A::ApplicationDriver: gui::window::AppDriverWindowManager,
{
#[inline]
pub fn window_manager(&self) -> &dyn gui::window::WindowManager {
use gui::window::AppDriverWindowManager;
self.app_driver.window_manager()
}
}
pub enum EngineJoinHandleError {
Panicked(Box<dyn Any + Send + 'static>),
InvalidStageTransition(InvalidEngineStageTransition),
}
impl Debug for EngineJoinHandleError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Panicked(_) =>
f.debug_tuple("EngineJoinHandleError::Panicked")
.finish_non_exhaustive(),
Self::InvalidStageTransition(err) =>
f.debug_tuple("EngineJoinHandleError::InvalidStageTransition")
.field(err)
.finish(),
}
}
}
impl Display for EngineJoinHandleError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Panicked(_) => write!(f, "Engine thread panicked!"),
Self::InvalidStageTransition(err) => Display::fmt(err, f),
}
}
}
impl Error for EngineJoinHandleError {}
pub struct EngineJoinHandle<A: Application> {
join_handle: JoinHandle<()>,
engine: Arc<Engine<A>>,
}
impl<A: Application> EngineJoinHandle<A> {
pub fn join(self) -> Result<(), EngineJoinHandleError> {
self.join_handle.join()
.map_err(|err| EngineJoinHandleError::Panicked(err))
}
pub fn stop(self) -> Result<(), EngineJoinHandleError> {
self.engine.stop()
.map_err(|err| EngineJoinHandleError::InvalidStageTransition(err))?;
self.join()
}
#[inline]
pub fn engine(&self) -> Arc<Engine<A>> {
self.engine.clone()
}
}
pub struct EngineBuilder<A: Application> {
app: Option<A>,
app_driver: Option<A::ApplicationDriver>,
resources: Option<Arc<ResourceManager>>,
networking: Option<Arc<Networking<A::NetworkingDriver>>>,
}
impl<A: Application> EngineBuilder<A> {
pub fn new() -> Self {
Self {
app: None,
app_driver: None,
resources: None,
networking: None,
}
}
pub fn app(mut self, app: A) -> Self {
self.app = Some(app);
self
}
pub fn app_driver(mut self, app_driver: A::ApplicationDriver) -> Self {
self.app_driver = Some(app_driver);
self
}
pub fn resources(mut self, resources: Arc<ResourceManager>) -> Self {
self.resources = Some(resources);
self
}
pub fn networking_driver(
mut self,
driver_factory: impl NetDriverFactory<A::NetworkingDriver>,
) -> Self {
self.networking = Some(Arc::new(Networking::new(driver_factory)));
self
}
pub fn build(self) -> Arc<Engine<A>> {
let app = self.app
.expect(".app() is required");
let app_driver = self.app_driver
.expect(".app_driver() is required");
let resources = self.resources
.expect(".resources() is required");
let networking = self.networking
.expect(".networking_driver() is required");
let event_bus = Arc::new(EventBus::builder()
.event_type::<EngineStageChangedEvent>()
.build());
let state = Arc::new(RwLock::new(EngineStateInner::new(event_bus.clone())));
Arc::new(Engine {
app,
app_driver,
state,
event_bus,
resources,
networking,
})
}
pub fn spawn(self) -> EngineJoinHandle<A> {
let engine = self.build();
let engine_thread = engine.clone();
let join_handle = std::thread::Builder::new()
.spawn(move || {
engine_thread.start();
}).unwrap();
EngineJoinHandle {
join_handle,
engine,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct NonExhaustive(pub(crate) ());