fennel_engine/
app.rs

1use std::{fs, sync::{Arc, Mutex}};
2
3use fennel_core::{events::{KeyboardEvent, WindowEventHandler}, graphics::WindowConfig, Window};
4use serde::{Deserialize, Serialize};
5use specs::{Dispatcher, DispatcherBuilder, WorldExt};
6
7use crate::{ecs::{input::InputSystem, sprite::{HostPtr, RenderingSystem, Sprite}}, events::KeyEvents};
8
9/// The application struct which contains [`fennel_core::Window`], [`specs::World`] and `specs`
10/// `Dispatcher`
11pub struct App {
12    /// Responsible for GFX and audio
13    pub window: fennel_core::Window,
14    /// ECS world
15    pub world: specs::World,
16    /// ECS dispatcher
17    pub dispatcher: Dispatcher<'static, 'static>,
18}
19
20/// Builder for [`App`]
21#[derive(Default, Debug)]
22pub struct AppBuilder {
23    name: &'static str,
24    dimensions: (u32, u32),
25    config: &'static str,
26    window_config: WindowConfig
27}
28
29#[derive(Deserialize, Serialize, Debug)]
30struct Config {
31    assets_path: String
32}
33
34unsafe impl Send for App {}
35unsafe impl Sync for App {}
36
37#[async_trait::async_trait]
38impl WindowEventHandler for App {
39    fn update(&self, _window: &mut Window) -> anyhow::Result<()> {
40        Ok(())
41    }
42
43    fn draw(&mut self, window: &mut Window) -> anyhow::Result<()> {
44        self.frame_tick();
45        window.graphics.canvas.present();
46        Ok(())
47    }
48
49    fn key_down_event(&self, _window: &mut Window, event: KeyboardEvent) -> anyhow::Result<()> {
50        println!("{:?}", event.keycode);
51        Ok(())
52    }
53}
54
55impl App {
56    /// Runs the event loop, must be called only once, UB otherwise
57    pub async fn run(mut self) -> anyhow::Result<()> {
58        // you know what? fuck you and your borrow checker.
59        // i'm 100% sure this app is single-threaded and its 11 pm
60        // at the moment so i'm not gonna solve this shit in some
61        // safe way
62        // as long this works and doesn't SEGFAULTs i'll keep it 
63        let ptr: *mut App = &mut self as *mut App;
64        fennel_core::events::run(&mut self.window, unsafe { &mut *ptr as &mut App }).await;
65        Ok(())
66    }
67
68    /// Evaluate systems
69    pub fn frame_tick(&mut self) {
70        let host_ptr = HostPtr(self as *mut App);
71        self.world.insert(host_ptr);
72        self.dispatcher.dispatch(&self.world);
73        self.world.maintain();
74        self.world.remove::<HostPtr>();
75    }
76}
77
78impl AppBuilder {
79    /// Create a new [`AppBuilder`]
80    pub fn new() -> AppBuilder {
81        AppBuilder {
82            name: "",
83            dimensions: (100, 100),
84            config: "",
85            window_config: WindowConfig {
86                resizable: false,
87                fullscreen: false,
88                centered: false
89            }
90        }
91    }
92
93    /// Set the window name
94    pub fn name(mut self, name: &'static str) -> AppBuilder {
95        self.name = name;
96        self
97    }
98
99    /// Set the window dimensions
100    pub fn dimensions(mut self, dimensions: (u32, u32)) -> AppBuilder {
101        self.dimensions = dimensions;
102        self
103    }
104
105    /// Set the application config
106    pub fn config(mut self, path: &'static str) -> AppBuilder {
107        self.config = path;
108        self
109    }
110
111    /// Builds an [`App`]
112    pub fn build(self) -> anyhow::Result<App> {
113        let resource_manager = Arc::new(Mutex::new(fennel_core::resources::ResourceManager::new()));
114        let config_reader = fs::read(self.config)?;
115        let config: Config = toml::from_slice(&config_reader)?;
116        let graphics = fennel_core::graphics::Graphics::new(
117            self.name.to_string(),
118            self.dimensions,
119            resource_manager.clone(),
120            |graphics| {
121                resource_manager.lock().unwrap().load_dir(config.assets_path.clone().into(), graphics).unwrap();
122            },
123            self.window_config
124        );
125        let window = fennel_core::Window::new(
126            graphics.expect("failed to initialize graphics"),
127            resource_manager,
128        );
129        let mut world = specs::World::new();
130        let mut dispatcher = DispatcherBuilder::new()
131            .with_thread_local(RenderingSystem)
132            .with(InputSystem, "input_system", &[])
133            .build();
134        world.register::<Sprite>();
135        world.insert(KeyEvents::default());
136
137        dispatcher.setup(&mut world);
138
139        Ok(App {
140            window,
141            world,
142            dispatcher,
143        })
144    }
145}