fennel_engine/
app.rs

1use std::{
2    fs,
3    sync::{Arc, Mutex},
4};
5
6use fennel_core::{
7    Window,
8    events::{KeyboardEvent, WindowEventHandler},
9    graphics::WindowConfig,
10};
11use serde::{Deserialize, Serialize};
12use specs::{Builder, Dispatcher, DispatcherBuilder, WorldExt};
13
14use crate::{
15    ecs::{
16        input::InputSystem,
17        scene::SceneSystem,
18        sprite::{HostPtr, RenderingSystem, Sprite, SpriteFactory},
19    },
20    events::KeyEvents,
21    registry::ComponentRegistry,
22    scenes::Scene,
23};
24
25/// The application struct which contains [`fennel_core::Window`], [`specs::World`] and `specs`
26/// `Dispatcher`
27pub struct App {
28    /// Responsible for GFX and audio
29    pub window: fennel_core::Window,
30    /// ECS world
31    pub world: specs::World,
32    /// ECS dispatcher
33    pub dispatcher: Dispatcher<'static, 'static>,
34    /// Application scenes
35    pub scenes: Vec<Scene>,
36    /// Registry of component factories for scene drawing
37    pub component_registry: ComponentRegistry,
38}
39
40/// Builder for [`App`]
41#[derive(Default, Debug)]
42pub struct AppBuilder {
43    name: &'static str,
44    dimensions: (u32, u32),
45    config: &'static str,
46    window_config: WindowConfig,
47}
48
49/// Application config defined by user
50#[derive(Deserialize, Serialize, Debug)]
51struct Config {
52    /// Path to assets directory
53    assets_path: String,
54    /// Path to scenes directory
55    scenes_path: String,
56    /// First scene to display
57    initial_scene: String,
58}
59
60unsafe impl Send for App {}
61unsafe impl Sync for App {}
62
63#[async_trait::async_trait]
64impl WindowEventHandler for App {
65    fn update(&self, _window: &mut Window) -> anyhow::Result<()> {
66        Ok(())
67    }
68
69    fn draw(&mut self, window: &mut Window) -> anyhow::Result<()> {
70        self.frame_tick();
71        window.graphics.canvas.present();
72        Ok(())
73    }
74
75    fn key_down_event(&self, _window: &mut Window, event: KeyboardEvent) -> anyhow::Result<()> {
76        println!("{:?}", event.keycode);
77        Ok(())
78    }
79}
80
81impl App {
82    /// Runs the event loop, must be called only once, UB otherwise
83    pub async fn run(mut self) -> anyhow::Result<()> {
84        // you know what? fuck you and your borrow checker.
85        // i'm 100% sure this app is single-threaded and its 11 pm
86        // at the moment so i'm not gonna solve this shit in some
87        // safe way
88        // as long this works and doesn't SEGFAULTs i'll keep it
89        let ptr: *mut App = &mut self as *mut App;
90        fennel_core::events::run(&mut self.window, unsafe { &mut *ptr as &mut App }).await;
91        Ok(())
92    }
93
94    /// Evaluate systems
95    pub fn frame_tick(&mut self) {
96        let host_ptr = HostPtr(self as *mut App);
97        self.world.insert(host_ptr);
98        self.dispatcher.dispatch(&self.world);
99        self.world.maintain();
100        self.world.remove::<HostPtr>();
101    }
102}
103
104impl AppBuilder {
105    /// Create a new [`AppBuilder`]
106    pub fn new() -> AppBuilder {
107        AppBuilder {
108            name: "",
109            dimensions: (100, 100),
110            config: "",
111            window_config: WindowConfig {
112                resizable: false,
113                fullscreen: false,
114                centered: false,
115            },
116        }
117    }
118
119    /// Set the window name
120    pub fn name(mut self, name: &'static str) -> AppBuilder {
121        self.name = name;
122        self
123    }
124
125    /// Set the window dimensions
126    pub fn dimensions(mut self, dimensions: (u32, u32)) -> AppBuilder {
127        self.dimensions = dimensions;
128        self
129    }
130
131    /// Set the application config
132    pub fn config(mut self, path: &'static str) -> AppBuilder {
133        self.config = path;
134        self
135    }
136
137    /// Builds an [`App`]
138    pub fn build(self) -> anyhow::Result<App> {
139        let resource_manager = Arc::new(Mutex::new(fennel_core::resources::ResourceManager::new()));
140        let config_reader = fs::read(self.config)?;
141        let config: Config = toml::from_slice(&config_reader)?;
142        let graphics = fennel_core::graphics::Graphics::new(
143            self.name.to_string(),
144            self.dimensions,
145            resource_manager.clone(),
146            |graphics| {
147                resource_manager.lock().unwrap().load_dir(config.assets_path.clone().into(), graphics).unwrap();
148            },
149            self.window_config
150        );
151        let window = fennel_core::Window::new(
152            graphics.expect("failed to initialize graphics"),
153            resource_manager,
154        );
155        let mut component_registry = ComponentRegistry::new();
156        let mut world = specs::World::new();
157        let mut dispatcher = DispatcherBuilder::new()
158            .with_thread_local(RenderingSystem)
159            .with(SceneSystem, "scene_system", &[])
160            .with(InputSystem, "input_system", &[])
161            .build();
162        let mut scenes: Vec<Scene> = vec![];
163
164        component_registry.register("sprite", Box::new(SpriteFactory));
165        world.register::<Scene>();
166        world.register::<Sprite>();
167        world.insert(KeyEvents::default());
168
169        for entry in fs::read_dir(config.scenes_path).expect("meow") {
170            let scene_reader = fs::read(entry.unwrap().path()).expect("meow");
171            let scene: Scene = ron::de::from_bytes(&scene_reader)?; 
172            world.create_entity().with(scene.clone()).build();
173            scenes.push(scene.clone());
174
175            for entity in &scene.entities {
176                for component in &entity.components {
177                    let factory = component_registry.get(&component.id)
178                        .unwrap_or_else(|| { panic!("no factory {} found", component.id) });
179                    let entity = world.create_entity().build();
180                    factory.insert(&mut world, entity, &component.config);
181                }
182            }
183        }
184
185        dispatcher.setup(&mut world);
186
187        Ok(App {
188            window,
189            world,
190            dispatcher,
191            scenes,
192            component_registry
193        })
194    }
195}