limnus_app/
app.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use limnus_local_resource::{LocalResource, LocalResourceStorage};
6use limnus_message::{Message, MessageId, MessageStorage, Messages, MessagesIterator};
7use limnus_resource::prelude::*;
8use limnus_scheduler::Scheduler;
9use limnus_scheduler_runner::Runner;
10use limnus_stage::{Stage, StageTag, Stages};
11use limnus_system::{IntoSystem, SystemParam};
12use limnus_system_state::State;
13use std::any::type_name;
14use tracing::{debug, info};
15
16type AppRunner = dyn FnOnce(App) -> AppReturnValue;
17
18pub enum AppPhase {
19    WaitingForPlugins,
20    Running,
21}
22
23pub struct App {
24    app_runner: Option<Box<AppRunner>>,
25    schedulers_runner: Runner,
26    plugins: Vec<Box<dyn Plugin>>,
27    state: State,
28    phase: AppPhase,
29    stages: Stages,
30}
31
32impl App {
33    pub(crate) fn internal_add_plugin(&mut self, boxed_plugin: Box<dyn Plugin>) {
34        boxed_plugin.build(self);
35        debug!(plugin=?boxed_plugin, "Added");
36        self.plugins.push(boxed_plugin);
37    }
38
39    pub fn update(&mut self) {
40        if matches!(self.phase, AppPhase::WaitingForPlugins) {
41            let mut all_are_ready = true;
42
43            for plugin in &self.plugins {
44                if !plugin.is_initialized(self) {
45                    info!("...waiting for {plugin:?}");
46                    all_are_ready = false;
47                }
48            }
49
50            if !all_are_ready {
51                return;
52            }
53            debug!("all plugins are ready, starting post initialization");
54            let mut plugins = std::mem::take(&mut self.plugins); // Temporarily take ownership of the plugins
55            for plugin in &mut plugins {
56                plugin.post_initialization(self);
57            }
58
59            info!("...post initialization complete. start running systems in schedules!");
60
61            self.plugins = plugins;
62
63            self.phase = AppPhase::Running;
64        }
65
66        self.schedulers_runner
67            .run_schedulers(&self.stages, &mut self.state);
68    }
69}
70
71#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
72pub enum AppReturnValue {
73    Value(u32),
74}
75
76#[derive(Resource, Debug)]
77pub struct ApplicationExit {
78    pub value: AppReturnValue,
79}
80
81impl Default for App {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl App {
88    #[must_use]
89    pub fn new() -> Self {
90        Self {
91            app_runner: None,
92            state: State::new(),
93            plugins: Vec::default(),
94            phase: AppPhase::WaitingForPlugins,
95            schedulers_runner: Runner::new(),
96            stages: Stages::new(),
97        }
98    }
99
100    #[must_use]
101    pub fn empty() -> Self {
102        Self {
103            app_runner: None,
104            state: State::new(),
105            plugins: Vec::default(),
106            phase: AppPhase::WaitingForPlugins,
107            schedulers_runner: Runner::new(),
108            stages: Stages::new(),
109        }
110    }
111
112    /// Consume self and start the runner function. It is not certain that it will ever return.
113    pub fn run(&mut self) -> AppReturnValue {
114        // Replace the runner with Dummy to take ownership
115        let runner = self.app_runner.take();
116
117        // Replace self with an empty App to take ownership and get the current self returned.
118        let app = core::mem::replace(self, Self::empty());
119
120        runner.unwrap()(app)
121    }
122
123    pub fn add_plugins<P: PluginCollection>(&mut self, collection: P) -> &mut Self {
124        collection.attach_to_app(self);
125        self
126    }
127
128    pub fn add_stage<S>(&mut self)
129    where
130        S: StageTag,
131    {
132        let stage = Stage::new();
133        self.stages.add::<S>(stage);
134    }
135
136    /// # Panics
137    /// a `Stage` for the type parameter `S` must exist
138    pub fn add_system<F, Params, S>(&mut self, _stage_tag: S, system: F)
139    where
140        F: IntoSystem<Params>,
141        Params: SystemParam,
142        S: StageTag,
143    {
144        self.stages
145            .get_mut::<S>()
146            .expect("could not find stage")
147            .add_system(system);
148    }
149
150    pub fn add_scheduler<T>(&mut self, scheduler: T)
151    where
152        T: Scheduler,
153    {
154        self.schedulers_runner.add_scheduler(scheduler);
155    }
156
157    /// The function supplied by `app_runner` can in some scenarios never return.
158    pub fn set_runner(
159        &mut self,
160        app_runner: impl FnOnce(Self) -> AppReturnValue + 'static,
161    ) -> &mut Self {
162        self.app_runner = Some(Box::new(app_runner));
163        self
164    }
165
166    pub fn insert_resource<R: Resource>(&mut self, value: R) -> &mut Self {
167        debug!(resource_type=type_name::<R>(), value=?value, "inserting resource");
168        self.state.resources_mut().insert(value);
169        self
170    }
171
172    pub fn insert_local_resource<R: LocalResource>(&mut self, value: R) -> &mut Self {
173        debug!(resource_type=type_name::<R>(), value=?value, "inserting local resource");
174        self.state.local_resources_mut().insert(value);
175        self
176    }
177
178    #[inline]
179    pub fn resource_take<R: Resource>(&mut self) -> R {
180        self.state.resources_mut().remove::<R>().unwrap()
181    }
182
183    #[inline]
184    #[must_use]
185    pub fn get_resource_ref<R: Resource>(&self) -> Option<&R> {
186        self.state.resources().get::<R>()
187    }
188
189    #[inline]
190    pub fn get_resource_mut<R: Resource>(&mut self) -> Option<&mut R> {
191        self.state.resources_mut().get_mut::<R>()
192    }
193
194    #[inline]
195    #[must_use]
196    pub fn resource<R: Resource>(&self) -> &R {
197        self.state.resources().fetch::<R>()
198    }
199
200    #[inline]
201    pub fn resource_mut<R: Resource>(&mut self) -> &mut R {
202        self.state.resources_mut().fetch_mut::<R>()
203    }
204
205    #[must_use]
206    pub const fn resources(&self) -> &ResourceStorage {
207        self.state.resources()
208    }
209
210    pub fn resources_mut(&mut self) -> &mut ResourceStorage {
211        self.state.resources_mut()
212    }
213
214    #[must_use]
215    pub const fn local_resources(&self) -> &LocalResourceStorage {
216        self.state.local_resources()
217    }
218
219    #[inline]
220    #[must_use]
221    pub fn has_resource<R: Resource>(&self) -> bool {
222        self.state.resources().contains::<R>()
223    }
224
225    pub fn create_message_type<M: Message>(&mut self) {
226        debug!(channel_type = type_name::<M>(), "creating message queue");
227        self.state.messages_mut().register_message_type::<M>();
228    }
229
230    #[must_use]
231    pub fn get_messages<M: Message>(&self) -> Option<&Messages<M>> {
232        self.state.messages().get::<M>()
233    }
234
235    pub fn send<M: Message>(&mut self, message: M) -> MessageId<M> {
236        self.state
237            .messages_mut()
238            .get_mut::<M>()
239            .unwrap()
240            .send(message)
241    }
242
243    #[must_use]
244    pub const fn messages(&self) -> &MessageStorage {
245        self.state.messages()
246    }
247
248    pub fn messages_mut(&mut self) -> &mut MessageStorage {
249        self.state.messages_mut()
250    }
251
252    #[must_use]
253    pub fn iter_current<M: Message>(&self) -> MessagesIterator<M> {
254        self.state.messages().get::<M>().unwrap().iter_current()
255    }
256
257    #[must_use]
258    pub fn iter_previous<M: Message>(&self) -> MessagesIterator<M> {
259        self.state.messages().get::<M>().unwrap().iter_previous()
260    }
261}
262
263pub trait PluginCollection {
264    fn attach_to_app(self, app: &mut App);
265}
266
267impl<T: Plugin> PluginCollection for T {
268    fn attach_to_app(self, app: &mut App) {
269        let boxed = Box::new(self);
270        app.internal_add_plugin(boxed);
271    }
272}
273
274impl<T1: Plugin, T2: Plugin> PluginCollection for (T1, T2) {
275    fn attach_to_app(self, app: &mut App) {
276        let boxed_plugin1 = Box::new(self.0);
277        let boxed_plugin2 = Box::new(self.1);
278
279        app.internal_add_plugin(boxed_plugin1);
280        app.internal_add_plugin(boxed_plugin2);
281    }
282}
283
284impl<T1: Plugin, T2: Plugin, T3: Plugin> PluginCollection for (T1, T2, T3) {
285    fn attach_to_app(self, app: &mut App) {
286        let boxed_plugin1 = Box::new(self.0);
287        let boxed_plugin2 = Box::new(self.1);
288        let boxed_plugin3 = Box::new(self.2);
289
290        app.internal_add_plugin(boxed_plugin1);
291        app.internal_add_plugin(boxed_plugin2);
292        app.internal_add_plugin(boxed_plugin3);
293    }
294}
295
296impl<T1: Plugin, T2: Plugin, T3: Plugin, T4: Plugin> PluginCollection for (T1, T2, T3, T4) {
297    fn attach_to_app(self, app: &mut App) {
298        let boxed_plugin1 = Box::new(self.0);
299        let boxed_plugin2 = Box::new(self.1);
300        let boxed_plugin3 = Box::new(self.2);
301        let boxed_plugin4 = Box::new(self.3);
302
303        app.internal_add_plugin(boxed_plugin1);
304        app.internal_add_plugin(boxed_plugin2);
305        app.internal_add_plugin(boxed_plugin3);
306        app.internal_add_plugin(boxed_plugin4);
307    }
308}
309
310impl<T1: Plugin, T2: Plugin, T3: Plugin, T4: Plugin, T5: Plugin> PluginCollection
311    for (T1, T2, T3, T4, T5)
312{
313    fn attach_to_app(self, app: &mut App) {
314        let boxed_plugin1 = Box::new(self.0);
315        let boxed_plugin2 = Box::new(self.1);
316        let boxed_plugin3 = Box::new(self.2);
317        let boxed_plugin4 = Box::new(self.3);
318        let boxed_plugin5 = Box::new(self.4);
319
320        app.internal_add_plugin(boxed_plugin1);
321        app.internal_add_plugin(boxed_plugin2);
322        app.internal_add_plugin(boxed_plugin3);
323        app.internal_add_plugin(boxed_plugin4);
324        app.internal_add_plugin(boxed_plugin5);
325    }
326}
327
328/// Plugins are not allowed to mutate themselves, just reference the app
329pub trait Plugin: 'static {
330    // Send + Sync +
331    fn build(&self, _app: &mut App) {}
332
333    fn is_initialized(&self, _app: &App) -> bool {
334        true
335    }
336
337    fn post_initialization(&self, _app: &mut App) {}
338
339    fn type_name(&self) -> &'static str {
340        type_name::<Self>()
341    }
342}
343
344impl std::fmt::Debug for dyn Plugin {
345    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346        write!(f, "{}", self.type_name())
347    }
348}