use the graph system dispatcher to execute systems, make it easier to add systems
This commit is contained in:
parent
4c6edff639
commit
f7a455997d
|
@ -9,50 +9,42 @@ pub mod components;
|
|||
|
||||
/// A trait that represents a simple system
|
||||
pub trait SimpleSystem {
|
||||
fn setup(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// todo: make async?
|
||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
pub type SystemFn = dyn Fn(&mut World) -> anyhow::Result<()>;
|
||||
|
||||
pub struct SystemFnExecutor {
|
||||
systems: HashMap<String, Box<SystemFn>>,
|
||||
}
|
||||
|
||||
impl SystemFnExecutor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
systems: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_systems(systems: HashMap<String, Box<SystemFn>>) -> Self {
|
||||
Self {
|
||||
systems,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_system(&mut self, system_label: &str, system: Box<SystemFn>) {
|
||||
self.systems.insert(system_label.to_string(), system);
|
||||
}
|
||||
|
||||
pub fn remove_system(&mut self, system_label: &str) {
|
||||
self.systems.remove(&system_label.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleSystem for SystemFnExecutor {
|
||||
impl<S> SimpleSystem for S
|
||||
where S: FnMut(&mut World) -> anyhow::Result<()>
|
||||
{
|
||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||
for system in self.systems.iter() {
|
||||
let system_fn = system.1.as_ref();
|
||||
let res = system_fn(world);
|
||||
|
||||
// log an error returned by the system
|
||||
if let Err(e) = res {
|
||||
warn!("System execution of {} resulted in an error! '{}'", system.0, e);
|
||||
self(world)
|
||||
}
|
||||
}
|
||||
|
||||
/// A system that executes a batch of systems in order that they were given.
|
||||
pub struct BatchedSystem {
|
||||
systems: Vec<Box<dyn SimpleSystem>>,
|
||||
}
|
||||
|
||||
impl BatchedSystem {
|
||||
/// Create a new BatchedSystems. The systems will be executed in the order given.
|
||||
pub fn new(systems: Vec<Box<dyn SimpleSystem>>) -> Self {
|
||||
Self {
|
||||
systems
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleSystem for BatchedSystem {
|
||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||
for system in self.systems.iter_mut() {
|
||||
system.execute_mut(world)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -67,12 +59,16 @@ struct SystemGraphEdge {
|
|||
|
||||
}
|
||||
|
||||
pub struct SystemGraphExecutor {
|
||||
/// Dispatcher for multiple systems.
|
||||
///
|
||||
/// This struct uses a graph for finding executing systems with dependencies.
|
||||
/// At some point this will be multithreaded.
|
||||
pub struct SystemDispatcher {
|
||||
graph: StableDiGraph<SystemGraphNode, SystemGraphEdge>,
|
||||
node_refs: HashMap<String, NodeIndex>,
|
||||
}
|
||||
|
||||
impl Default for SystemGraphExecutor {
|
||||
impl Default for SystemDispatcher {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
graph: StableDiGraph::new(),
|
||||
|
@ -81,7 +77,7 @@ impl Default for SystemGraphExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
impl SystemGraphExecutor {
|
||||
impl SystemDispatcher {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
@ -92,11 +88,11 @@ impl SystemGraphExecutor {
|
|||
/// and will cause a panic later on in the engine.
|
||||
///
|
||||
/// TODO: Return false if a cycle was created, making it impossible to know when to dispatch them. This will mean that the System would not of had been added.
|
||||
pub fn add_system(&mut self, name: String, system: Box<dyn SimpleSystem>, dependencies: &[String]) -> bool {
|
||||
pub fn add_system(&mut self, name: String, system: Box<dyn SimpleSystem>, dependencies: Vec<String>) -> bool {
|
||||
let added_index =
|
||||
self.graph.add_node(SystemGraphNode {
|
||||
name: name.clone(),
|
||||
dependent_on: dependencies.clone().to_vec(),
|
||||
dependent_on: dependencies.clone(),
|
||||
system,
|
||||
});
|
||||
|
||||
|
@ -106,7 +102,7 @@ impl SystemGraphExecutor {
|
|||
// find the dependencies in node_refs and add edges directed towards the new node
|
||||
for depend in dependencies.into_iter() {
|
||||
let idx = self.node_refs
|
||||
.get(depend)
|
||||
.get(&depend)
|
||||
.expect("Dependency not found!");
|
||||
|
||||
self.graph.add_edge(idx.clone(), added_index, SystemGraphEdge { });
|
||||
|
@ -115,7 +111,7 @@ impl SystemGraphExecutor {
|
|||
true
|
||||
}
|
||||
|
||||
pub(crate) fn execute_mut(&mut self, world: &mut World) {
|
||||
pub(crate) fn execute_systems(&mut self, world: &mut World) {
|
||||
let mut topo = Topo::new(&self.graph);
|
||||
|
||||
while let Some(nx) = topo.next(&self.graph) {
|
||||
|
@ -125,6 +121,10 @@ impl SystemGraphExecutor {
|
|||
Ok(()) => {},
|
||||
Err(e) => {
|
||||
warn!("System execution of {} resulted in an error! '{}'.", node.name, e);
|
||||
|
||||
// TODO: Find some way to stop traversing down a topopath in the graph executor and continue down a different one.
|
||||
// This might require a custom topopath implementation (preferably iterative). It would have to ensure that it
|
||||
// doesn't run other systems that are dependent on that failed system.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -132,9 +132,9 @@ impl SystemGraphExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
impl SimpleSystem for SystemGraphExecutor {
|
||||
impl SimpleSystem for SystemDispatcher {
|
||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||
SystemGraphExecutor::execute_mut(self, world);
|
||||
self.execute_systems(world);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
43
src/game.rs
43
src/game.rs
|
@ -4,7 +4,7 @@ use async_std::{task::block_on, sync::Mutex};
|
|||
|
||||
use hecs::World;
|
||||
use instant::Instant;
|
||||
use tracing::{metadata::LevelFilter, info, debug, warn};
|
||||
use tracing::{metadata::LevelFilter, info, debug, warn, error};
|
||||
use tracing_subscriber::{
|
||||
layer::{Layer, SubscriberExt},
|
||||
filter::FilterFn,
|
||||
|
@ -13,7 +13,7 @@ use tracing_subscriber::{
|
|||
|
||||
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
|
||||
|
||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SystemFnExecutor, SimpleSystem, SystemGraphExecutor}};
|
||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher}};
|
||||
|
||||
struct TickCounter {
|
||||
counter: u32,
|
||||
|
@ -76,20 +76,20 @@ struct GameLoop {
|
|||
|
||||
world: Arc<Mutex<World>>,
|
||||
/// higher priority systems
|
||||
engine_systems: Vec<Box<dyn SimpleSystem>>,
|
||||
systems: Vec<Box<dyn SimpleSystem>>,
|
||||
engine_sys_dispatcher: SystemDispatcher,
|
||||
user_sys_dispatcher: SystemDispatcher,
|
||||
fps_counter: TickCounter,
|
||||
}
|
||||
|
||||
impl GameLoop {
|
||||
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: Vec<Box<dyn SimpleSystem>>) -> GameLoop {
|
||||
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: SystemDispatcher) -> GameLoop {
|
||||
Self {
|
||||
window: Arc::clone(&window),
|
||||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
||||
|
||||
world,
|
||||
engine_systems: vec![],
|
||||
systems: user_systems,
|
||||
engine_sys_dispatcher: SystemDispatcher::new(),
|
||||
user_sys_dispatcher: user_systems,
|
||||
fps_counter: TickCounter::new(),
|
||||
}
|
||||
}
|
||||
|
@ -105,13 +105,20 @@ impl GameLoop {
|
|||
async fn update(&mut self) {
|
||||
let mut world = self.world.lock().await;
|
||||
|
||||
for esystem in self.engine_systems.iter_mut() {
|
||||
if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut world) {
|
||||
error!("Error when executing engine ecs systems: '{}'", e);
|
||||
}
|
||||
|
||||
if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut world) {
|
||||
error!("Error when executing user ecs systems: '{}'", e);
|
||||
}
|
||||
/* for esystem in self.engine_systems.iter_mut() {
|
||||
esystem.execute_mut(&mut world).unwrap();
|
||||
}
|
||||
|
||||
for system in self.systems.iter_mut() {
|
||||
system.execute_mut(&mut world).unwrap();
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
|
||||
|
@ -218,14 +225,14 @@ impl GameLoop {
|
|||
|
||||
pub struct Game {
|
||||
world: Option<Arc<Mutex<World>>>,
|
||||
systems: Option<Vec<Box<dyn SimpleSystem>>>
|
||||
system_dispatcher: Option<SystemDispatcher>
|
||||
}
|
||||
|
||||
impl Default for Game {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
world: None,
|
||||
systems: Some(vec![]),
|
||||
system_dispatcher: Some(SystemDispatcher::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,12 +266,18 @@ impl Game {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_system<S>(&mut self, system: S) -> &mut Self
|
||||
pub fn with_system<S>(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self
|
||||
where
|
||||
S: SimpleSystem + 'static
|
||||
{
|
||||
let systems = self.systems.as_mut().unwrap();
|
||||
systems.push(Box::new(system));
|
||||
|
||||
let depends: Vec<String> = depends
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
let dispatcher = self.system_dispatcher.as_mut().unwrap();
|
||||
dispatcher.add_system(name.to_string(), Box::new(system), depends);
|
||||
|
||||
self
|
||||
}
|
||||
|
@ -275,7 +288,7 @@ impl Game {
|
|||
let event_loop = EventLoop::new();
|
||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
||||
|
||||
let systems = self.systems.take().unwrap();
|
||||
let systems = self.system_dispatcher.take().unwrap();
|
||||
|
||||
let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await;
|
||||
|
||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -12,12 +12,10 @@ use game::Game;
|
|||
use hecs::World;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::ecs::SystemFnExecutor;
|
||||
use crate::render::material::Material;
|
||||
use crate::render::texture::Texture;
|
||||
use crate::ecs::components::camera::CameraComponent;
|
||||
use crate::math::{Angle, Transform};
|
||||
//use specs::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Point2d {
|
||||
|
@ -101,32 +99,8 @@ async fn main() {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
let mut fn_exec = SystemFnExecutor::new();
|
||||
fn_exec.add_system("jiggle", Box::new(jiggle_system));
|
||||
|
||||
Game::initialize().await
|
||||
.with_world(world)
|
||||
.with_system(fn_exec)
|
||||
.with_system("jiggle", jiggle_system, &[])
|
||||
.run().await;
|
||||
|
||||
/* world.register::<Point2d>();
|
||||
world.register::<Point3d>();
|
||||
|
||||
world.create_entity()
|
||||
.with(Point2d::new(10, 10))
|
||||
.with(Point3d::new(50, 50, 50))
|
||||
.build();
|
||||
|
||||
let mut dispatcher = DispatcherBuilder::new().with(TestingSystem, "testing_system", &[]).build();
|
||||
dispatcher.setup(&mut world);
|
||||
|
||||
//dispatcher.dispatch(&mut world);
|
||||
|
||||
Game::initialize().await
|
||||
.with_world(world)
|
||||
.with_dispatcher(dispatcher)
|
||||
.run().await; */
|
||||
|
||||
/* let mut game = Game::initialize().await;
|
||||
game.run().await; */
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue