From e96cb3585b71cfd512713ece2d95c3cd06fcce05 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 9 Sep 2023 10:51:58 -0400 Subject: [PATCH 1/2] Start working on game plugins --- src/app.rs | 3 ++ src/game.rs | 131 ++++++++++++++++++-------------------------------- src/input.rs | 46 ++++++++++-------- src/lib.rs | 4 +- src/plugin.rs | 7 +++ 5 files changed, 86 insertions(+), 105 deletions(-) create mode 100644 src/app.rs create mode 100644 src/plugin.rs diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..85af48d --- /dev/null +++ b/src/app.rs @@ -0,0 +1,3 @@ +pub struct App { + +} \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index b4aaa8a..01ed209 100755 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, collections::VecDeque}; use async_std::{task::block_on, sync::Mutex}; @@ -14,7 +14,7 @@ use tracing_subscriber::{ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}}; -use crate::{render::renderer::{Renderer, BasicRenderer}, input_event::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, input::InputSystem}; +use crate::{render::renderer::{Renderer, BasicRenderer}, input_event::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, input::InputSystem, plugin::Plugin}; pub struct Controls<'a> { pub world: &'a mut edict::World, @@ -36,14 +36,14 @@ struct GameLoop { window: Arc, renderer: Box, - world: Arc>, + world: edict::World, /// higher priority systems engine_sys_dispatcher: SystemDispatcher, user_sys_dispatcher: SystemDispatcher, } impl GameLoop { - pub async fn new(window: Arc, world: Arc>, user_systems: SystemDispatcher) -> GameLoop { + pub async fn new(window: Arc, world: edict::World, user_systems: SystemDispatcher) -> GameLoop { Self { window: Arc::clone(&window), renderer: Box::new(BasicRenderer::create_with_window(window).await), @@ -60,8 +60,7 @@ impl GameLoop { pub async fn on_init(&mut self) { // Create the EventQueue resource in the world - let mut world = self.world.lock().await; - world.insert_resource(EventQueue::new()); + self.world.insert_resource(EventQueue::new()); } pub fn run_sync(&mut self, event: Event<()>, control_flow: &mut ControlFlow) { @@ -69,10 +68,8 @@ impl GameLoop { } async fn update(&mut self) { - let mut world = self.world.lock().await; - let mut controls = Controls { - world: &mut world, + world: &mut self.world, }; if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut controls) { @@ -102,18 +99,14 @@ impl GameLoop { // TODO: Create system for this? or maybe merge into input system, idk InputEvent::CursorEntered { .. } => { - let mut world = self.world.lock().await; - - let mut state = world.with_resource(|| WindowState::new()); + let mut state = self.world.with_resource(|| WindowState::new()); state.is_cursor_inside_window = true; None }, InputEvent::CursorLeft { .. } => { - let mut world = self.world.lock().await; - - let mut state = world.with_resource(|| WindowState::new()); + let mut state = self.world.with_resource(|| WindowState::new()); state.is_cursor_inside_window = false; None @@ -127,14 +120,6 @@ impl GameLoop { } } - fn render_window() { - todo!() - } - - fn render_item() { - todo!() - } - fn on_exit(&mut self) { info!("On exit!"); } @@ -145,16 +130,14 @@ impl GameLoop { Event::DeviceEvent { device_id, event } => match event { // convert a MouseMotion event to an InputEvent DeviceEvent::MouseMotion { delta } => { - let mut world = self.world.lock().await; - // make sure that the mouse is inside the window and the mouse has focus before reporting mouse motion - let trigger = match world.get_resource::() { + let trigger = match self.world.get_resource::() { Some(window_state) if window_state.is_focused && window_state.is_cursor_inside_window => true, _ => false, }; if trigger { - let event_queue = world.with_resource(|| Events::::new()); + let event_queue = self.world.with_resource(|| Events::::new()); let input_event = InputEvent::MouseMotion { device_id, delta, }; event_queue.push_back(input_event); @@ -173,8 +156,7 @@ impl GameLoop { // Its possible to receive multiple input events before the update event for // the InputSystem is called, so we must use a queue for the events. { - let world = self.world.lock().await; - if let Some(mut event_queue) = world.get_resource_mut::() { + if let Some(mut event_queue) = self.world.get_resource_mut::() { event_queue.trigger_event(input_event.clone()); }; } @@ -199,9 +181,7 @@ impl GameLoop { }, WindowEvent::Focused(is_focused) => { - let mut world = self.world.lock().await; - - let mut state = world.with_resource(|| WindowState::new()); + let mut state = self.world.with_resource(|| WindowState::new()); state.is_focused = *is_focused; }, @@ -219,12 +199,10 @@ impl GameLoop { debug!("FPS: {}fps, {:.2}ms/frame", fps, self.fps_counter.get_tick_time()); } */ - let mut world = self.world.lock().await; - self.renderer.as_mut().prepare(&mut world); - if let Some(mut event_queue) = world.get_resource_mut::() { + self.renderer.as_mut().prepare(&mut self.world); + if let Some(mut event_queue) = self.world.get_resource_mut::() { event_queue.update_events(); } - drop(world); match self.renderer.as_mut().render() { Ok(_) => {} @@ -246,14 +224,16 @@ impl GameLoop { } pub struct Game { - world: Option>>, + pub world: Option, + plugins: VecDeque>, system_dispatcher: Option, } impl Default for Game { fn default() -> Self { Self { - world: None, + world: Some(edict::World::new()), + plugins: VecDeque::new(), system_dispatcher: Some(SystemDispatcher::new()), } } @@ -261,55 +241,33 @@ impl Default for Game { impl Game { pub async fn initialize() -> Game { - /* let filter = FilterFn::new(|metadata| { - metadata.module_path() - .unwrap_or_else(|| metadata.target()) - .starts_with("lyra_engine") && (LevelFilter::DEBUG >= metadata.level().to_owned()) - }); - - let layer = tracing_subscriber::fmt::layer(); - - tracing_subscriber::registry() - .with(layer.with_filter(filter)) - .init(); */ - - - /* tracing_subscriber::registry() - .with(fmt::layer().with_writer(stdout_layer)) - .with(filter::Targets::new() - .with_target("lyra_engine", Level::TRACE) - .with_default(Level::DEBUG)) - .init(); */ - - /* tracing_subscriber::fmt() - .with_max_level(Level::DEBUG) - .init(); */ - - info!("dheiudfgbwehifwe"); - let mut def = Self::default(); - def.system_dispatcher.as_mut().unwrap().add_system("input", InputSystem::new(), &[]); + //def.system_dispatcher.add_system("input", InputSystem::new(), &[]); def } - pub fn with_world(&mut self, world: edict::World) -> &mut Self { - self.world = Some(Arc::new(Mutex::new(world))); - - self - } - - pub fn with_world_arc(&mut self, world: Arc>) -> &mut Self { - self.world = Some(world); - - self - } - pub fn with_system(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self where S: SimpleSystem + 'static { - let dispatcher = self.system_dispatcher.as_mut().unwrap(); - dispatcher.add_system(name, system, depends); + let system_dispatcher = self.system_dispatcher.as_mut().unwrap(); + system_dispatcher.add_system(name, system, depends); + + self + } + + /// Add a plugin to the game + pub fn with_plugin

(&mut self, plugin: P) -> &mut Self + where + P: Plugin + 'static + { + self.plugins.push_back(Box::new(plugin)); + + self + } + + pub fn with_world(&mut self, world: edict::World) -> &mut Self { + self.world = Some(world); self } @@ -323,16 +281,19 @@ impl Game { .with_target("lyra_engine", Level::TRACE) .with_default(Level::INFO)) .init(); - - let world = self.world.take().expect("ECS World was never given to Game!"); + // setup all the plugins + while let Some(plugin) = self.plugins.pop_front() { + plugin.as_ref().setup(self); + } + + // start winit event loops let event_loop = EventLoop::new(); let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let systems = self.system_dispatcher.take().unwrap(); - - let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await; - + let world = self.world.take().unwrap_or_else(|| edict::World::new()); + let system_dispatcher = self.system_dispatcher.take().unwrap(); + let mut g_loop = GameLoop::new(Arc::clone(&window), world, system_dispatcher).await; g_loop.on_init().await; event_loop.run(move |event, _, control_flow| { diff --git a/src/input.rs b/src/input.rs index 95b9410..4d71d08 100755 --- a/src/input.rs +++ b/src/input.rs @@ -5,7 +5,7 @@ use glam::Vec2; use tracing::{warn, debug}; use winit::event::{ElementState, MouseScrollDelta}; -use crate::{ecs::{SimpleSystem, EventQueue}, input_event::InputEvent}; +use crate::{ecs::{SimpleSystem, EventQueue}, input_event::InputEvent, plugin::Plugin}; pub type KeyCode = winit::event::VirtualKeyCode; @@ -84,8 +84,6 @@ impl From for Force { } } -/// -/// /// Translated `WindowEvent::Touch` from `winit` crate #[derive(Clone, Debug, PartialEq)] pub struct Touch { @@ -206,22 +204,6 @@ trait InputStorage { impl InputStorage for InputButtons { fn update_just_pressed(&mut self) { - /* for (hash, button) in self.button_events.iter_mut() { - - /* if let ButtonEvent::JustPressed(b) = button { - *button = ButtonEvent::Pressed(b.clone()); - } */ - match button { - ButtonEvent::Pressed(_) => todo!(), - ButtonEvent::Released(b) => { - return false; - }, - ButtonEvent::JustPressed(b) => { - *button = ButtonEvent::Pressed(b.clone()); - }, - } - } */ - self.button_events.retain(|_hash, button| { match button { // remove released, no need to keep those around. @@ -365,4 +347,30 @@ impl SimpleSystem for InputSystem { Ok(()) } +} + +fn input_system_fn(world: &mut edict::World) -> anyhow::Result<()> { + //let world = &mut controls.world; + + let queue = world.get_resource_mut::() + .map(|q| q.read_events::()).flatten(); + + if queue.is_none() { + return Ok(()); + } + + let mut events = queue.unwrap(); + let mut input = InputSystem::new(); + + while let Some(event) = events.pop_front() { + input.update(&event, world); + } + + Ok(()) +} + +impl Plugin for InputSystem { + fn setup(&self, game: &mut crate::game::Game) { + game.with_system("input", input_system_fn, &[]); + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 4540345..886723b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,6 @@ pub mod resources; pub mod ecs; pub mod math; pub mod input; -pub mod castable_any; \ No newline at end of file +pub mod castable_any; +pub mod plugin; +pub mod app; \ No newline at end of file diff --git a/src/plugin.rs b/src/plugin.rs new file mode 100644 index 0000000..762f441 --- /dev/null +++ b/src/plugin.rs @@ -0,0 +1,7 @@ +use crate::game::Game; + +pub trait Plugin { + /// Setup this plugin. This runs before the game has started + fn setup(&self, game: &mut Game); +} + From 71c1188f3a0e2eaed19642ba9349d15a78a7b2c6 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 10 Sep 2023 00:38:54 -0400 Subject: [PATCH 2/2] Finish implementing plugins, create default plugins, create input plugin --- Cargo.toml | 1 + src/app.rs | 3 --- src/ecs/events.rs | 2 +- src/game.rs | 7 +++-- src/input.rs | 46 +++++-------------------------- src/lib.rs | 16 ++++++++++- src/plugin.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 96 insertions(+), 48 deletions(-) delete mode 100644 src/app.rs diff --git a/Cargo.toml b/Cargo.toml index 79dd7d2..ba39438 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,4 @@ edict = "0.5.0" atomicell = "0.1.9" aligned-vec = "0.5.0" tracing-appender = "0.2.2" +stopwatch = "0.0.7" diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index 85af48d..0000000 --- a/src/app.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct App { - -} \ No newline at end of file diff --git a/src/ecs/events.rs b/src/ecs/events.rs index df4e06a..2374fd3 100644 --- a/src/ecs/events.rs +++ b/src/ecs/events.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, VecDeque}, any::{TypeId, Any}, cell::{RefCell, Ref, RefMut}}; +use std::{collections::{HashMap, VecDeque}, any::{TypeId, Any}, cell::RefCell}; use crate::castable_any::CastableAny; diff --git a/src/game.rs b/src/game.rs index 01ed209..ab157b2 100755 --- a/src/game.rs +++ b/src/game.rs @@ -241,9 +241,7 @@ impl Default for Game { impl Game { pub async fn initialize() -> Game { - let mut def = Self::default(); - //def.system_dispatcher.add_system("input", InputSystem::new(), &[]); - def + Self::default() } pub fn with_system(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self @@ -279,7 +277,8 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() .with_target("lyra_engine", Level::TRACE) - .with_default(Level::INFO)) + .with_target("wgpu_core", Level::INFO) + .with_default(Level::DEBUG)) .init(); // setup all the plugins diff --git a/src/input.rs b/src/input.rs index 4d71d08..fabdef8 100755 --- a/src/input.rs +++ b/src/input.rs @@ -221,26 +221,10 @@ impl InputStorage for InputButtons>>, // TODO -} +#[derive(Default)] +pub struct InputSystem; impl InputSystem { - pub(crate) fn new() -> Self { - let gilrs = match Gilrs::new() { - Ok(g) => Some(Arc::new(Mutex::new(g))), - Err(e) => { - warn!("Failure to initialize gilrs, gamepads will not work!\n{}", e); - - None - } - }; - - Self { - gilrs, - } - } - pub fn update(&mut self, event: &InputEvent, world: &mut edict::World) -> bool { let event_queue = world.get_resource_mut::(); if event_queue.is_none() { @@ -349,28 +333,12 @@ impl SimpleSystem for InputSystem { } } -fn input_system_fn(world: &mut edict::World) -> anyhow::Result<()> { - //let world = &mut controls.world; +/// Plugin that runs InputSystem +#[derive(Default)] +pub struct InputPlugin; - let queue = world.get_resource_mut::() - .map(|q| q.read_events::()).flatten(); - - if queue.is_none() { - return Ok(()); - } - - let mut events = queue.unwrap(); - let mut input = InputSystem::new(); - - while let Some(event) = events.pop_front() { - input.update(&event, world); - } - - Ok(()) -} - -impl Plugin for InputSystem { +impl Plugin for InputPlugin { fn setup(&self, game: &mut crate::game::Game) { - game.with_system("input", input_system_fn, &[]); + game.with_system("input", InputSystem::default(), &[]); } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 886723b..eefaaca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,18 @@ pub mod math; pub mod input; pub mod castable_any; pub mod plugin; -pub mod app; \ No newline at end of file + +use plugin::Plugin; + +use crate::input::InputPlugin; + +/// Default plugins of Lyra. Make sure to have these added to the Game first +#[derive(Default)] +pub struct DefaultPlugins; + +impl Plugin for DefaultPlugins { + fn setup(&self, game: &mut game::Game) { + // setup input + InputPlugin::default().setup(game); + } +} \ No newline at end of file diff --git a/src/plugin.rs b/src/plugin.rs index 762f441..1f28220 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,7 +1,76 @@ use crate::game::Game; +/// A Plugin is something you can add to a `Game` that can be used to define systems, or spawn initial entities. pub trait Plugin { /// Setup this plugin. This runs before the game has started fn setup(&self, game: &mut Game); } +impl

Plugin for P + where P: Fn(&mut Game) +{ + fn setup(&self, game: &mut Game) { + self(game); + } +} + +/// Represents a set of plugins that will be executed in order they are supplied. +#[derive(Default)] +pub struct PluginSet { + /// A set of plugins that will be executed in order + pub plugins: Vec>, +} + +impl PluginSet { + pub fn new() -> Self { + Self::default() + } + + pub fn add_plugin

(&mut self, plugin: P) -> &mut Self + where + P: Plugin + 'static + { + self.plugins.push(Box::new(plugin)); + + self + } +} + +impl Plugin for PluginSet { + fn setup(&self, game: &mut Game) { + for plugin in self.plugins.iter() { + plugin.setup(game); + } + } +} + +// Macro used for implementing PluginSet for tuples +macro_rules! impl_tuple_plugin_set { + ( $(($name: ident, $index: tt))+ ) => ( + impl<$($name: Plugin + 'static),+> From<($($name,)+)> for PluginSet { + fn from(value: ($($name,)+)) -> Self { + let plugins = vec![$(Box::new(value.$index) as Box<(dyn Plugin + 'static)>),+]; + + Self { + plugins, + } + } + } + ); +} + +impl_tuple_plugin_set! { (C0, 0) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) (C13, 13) } +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) (C13, 13) (C14, 14) } \ No newline at end of file