Skip to main content

fyrox_impl/engine/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Engine is container for all subsystems (renderer, ui, sound, resource manager). It also
22//! creates a window and an OpenGL context.
23
24#![warn(missing_docs)]
25
26pub mod error;
27pub mod executor;
28pub mod input;
29pub mod task;
30
31mod hotreload;
32mod wasm_utils;
33
34use crate::plugin::error::{GameError, GameResult};
35use crate::{
36    asset::{
37        event::ResourceEvent,
38        manager::{ResourceManager, ResourceWaitContext},
39        state::ResourceState,
40        untyped::{ResourceKind, UntypedResource},
41    },
42    core::{
43        algebra::Vector2,
44        err,
45        futures::{executor::block_on, future::join_all},
46        instant,
47        log::Log,
48        pool::Handle,
49        reflect::Reflect,
50        task::TaskPool,
51        variable::try_inherit_properties,
52        visitor::error::VisitError,
53        warn, SafeLock,
54    },
55    engine::{error::EngineError, input::InputState, task::TaskPoolHandler},
56    event::Event,
57    graph::{NodeMapping, SceneGraph},
58    graphics::error::FrameworkError,
59    gui::{
60        constructor::WidgetConstructorContainer,
61        font::{
62            loader::FontLoader, Font, BOLD_ITALIC, BUILT_IN_BOLD, BUILT_IN_FONT, BUILT_IN_ITALIC,
63        },
64        loader::UserInterfaceLoader,
65        style::{self, resource::StyleLoader, Style},
66        RenderMode, UiContainer, UiUpdateSwitches, UserInterface,
67    },
68    material::{
69        self,
70        loader::MaterialLoader,
71        shader::{loader::ShaderLoader, Shader, ShaderResource, ShaderResourceExtension},
72        Material,
73    },
74    plugin::{
75        dylib::DyLibDynamicPlugin, DynamicPlugin, Plugin, PluginContainer, PluginContext,
76        PluginRegistrationContext,
77    },
78    renderer::{ui_renderer::UiRenderInfo, Renderer},
79    resource::{
80        curve::{loader::CurveLoader, CurveResourceState},
81        gltf::material::GLTF_SHADER,
82        model::{loader::ModelLoader, Model, ModelResource},
83        texture::{
84            self, loader::TextureLoader, CompressionOptions, Texture, TextureImportOptions,
85            TextureKind, TextureMinificationFilter, TextureResource, TextureResourceExtension,
86        },
87    },
88    scene::{
89        base::NodeScriptMessage,
90        graph::{GraphUpdateSwitches, NodePool},
91        mesh::surface::{self, SurfaceData, SurfaceDataLoader},
92        navmesh,
93        node::{
94            constructor::{new_node_constructor_container, NodeConstructorContainer},
95            Node,
96        },
97        skybox::SkyBoxKind,
98        sound::SoundEngine,
99        tilemap::{
100            brush::{TileMapBrush, TileMapBrushLoader},
101            tileset::{TileSet, TileSetLoader},
102            CustomTileCollider, TileMapData,
103        },
104        Scene, SceneContainer, SceneLoader,
105    },
106    script::{
107        constructor::ScriptConstructorContainer, DynamicTypeId, PluginsRefMut, RoutingStrategy,
108        Script, ScriptContext, ScriptDeinitContext, ScriptMessage, ScriptMessageContext,
109        ScriptMessageKind, ScriptMessageSender, UniversalScriptContext,
110    },
111    window::Window,
112};
113use fxhash::{FxHashMap, FxHashSet};
114use fyrox_animation::AnimationTracksData;
115use fyrox_core::dyntype::DynTypeConstructorContainer;
116use fyrox_core::NameProvider;
117use fyrox_graphics::server::SharedGraphicsServer;
118use fyrox_graphics_gl::server::GlGraphicsServer;
119use fyrox_sound::{
120    buffer::{loader::SoundBufferLoader, SoundBuffer},
121    renderer::hrtf::{HrirSphereLoader, HrirSphereResourceData},
122};
123use std::{
124    any::TypeId,
125    cell::Cell,
126    collections::{HashSet, VecDeque},
127    fmt::{Display, Formatter},
128    io::Cursor,
129    ops::{Deref, DerefMut},
130    path::{Path, PathBuf},
131    rc::Rc,
132    sync::{
133        mpsc::{channel, Receiver, Sender},
134        Arc,
135    },
136    time::Duration,
137};
138use uuid::Uuid;
139use winit::event::{DeviceEvent, ElementState, WindowEvent};
140use winit::event_loop::{ActiveEventLoop, EventLoop};
141use winit::{
142    dpi::{Position, Size},
143    window::Icon,
144    window::WindowAttributes,
145};
146
147/// Serialization context holds runtime type information that allows to create unknown types using
148/// their UUIDs and a respective constructors.
149pub struct SerializationContext {
150    /// A node constructor container.
151    pub node_constructors: NodeConstructorContainer,
152    /// A script constructor container.
153    pub script_constructors: ScriptConstructorContainer,
154}
155
156impl Default for SerializationContext {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162impl SerializationContext {
163    /// Creates default serialization context.
164    pub fn new() -> Self {
165        Self {
166            node_constructors: new_node_constructor_container(),
167            script_constructors: ScriptConstructorContainer::new(),
168        }
169    }
170}
171
172/// Performance statistics.
173#[derive(Debug, Default)]
174pub struct PerformanceStatistics {
175    /// Amount of time spent in the UI system.
176    pub ui_time: Duration,
177
178    /// Amount of time spent in updating/initializing/destructing scripts of all scenes.
179    pub scripts_time: Duration,
180
181    /// Amount of time spent in plugins updating.
182    pub plugins_time: Duration,
183}
184
185impl Display for PerformanceStatistics {
186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187        writeln!(
188            f,
189            "Performance Statistics:\n\tUI: {:?}\n\tScripts: {:?}\n\tPlugins: {:?}",
190            self.ui_time, self.scripts_time, self.plugins_time
191        )
192    }
193}
194
195/// An initialized graphics context. It contains the main application window and the renderer instance.
196pub struct InitializedGraphicsContext {
197    /// Main application window.
198    pub window: Window,
199
200    /// Current renderer.
201    pub renderer: Renderer,
202
203    params: GraphicsContextParams,
204}
205
206impl InitializedGraphicsContext {
207    /// Tries to set a new icon for the window from the given data source. The data source must contain
208    /// some of the supported texture types data (png, bmp, jpg images). You can call this method
209    /// with [`include_bytes`] macro to pass file's data directly.
210    pub fn set_window_icon_from_memory(&mut self, data: &[u8]) {
211        if let Ok(texture) = TextureResource::load_from_memory(
212            Uuid::new_v4(),
213            ResourceKind::Embedded,
214            data,
215            TextureImportOptions::default()
216                .with_compression(CompressionOptions::NoCompression)
217                .with_minification_filter(TextureMinificationFilter::Linear),
218        ) {
219            self.set_window_icon_from_texture(&texture);
220        }
221    }
222
223    /// Tries to set a new icon for the window using the given texture.
224    pub fn set_window_icon_from_texture(&mut self, texture: &TextureResource) {
225        let data = texture.data_ref();
226        if let TextureKind::Rectangle { width, height } = data.kind() {
227            if let Ok(img) = Icon::from_rgba(data.data().to_vec(), width, height) {
228                self.window.set_window_icon(Some(img));
229            }
230        }
231    }
232}
233
234/// Graphics context of the engine, it could be in two main states:
235///
236/// - [`GraphicsContext::Initialized`] - active graphics context, that is fully initialized and ready for use.
237/// - [`GraphicsContext::Uninitialized`] - suspended graphics context, that contains a set of params that could
238/// be used for further initialization.
239///
240/// By default, when you creating an engine, there's no graphics context initialized. It must be initialized
241/// manually (if you need it) on [`Event::Resumed`]. On most operating systems, it is possible to initialize
242/// graphics context right after the engine was created. However Android won't allow you to do this, also on
243/// some versions of macOS immediate initialization could lead to panic.
244///
245/// You can switch between these states whenever you need, for example if your application does not need a
246/// window and a renderer at all you can just not create graphics context. This could be useful for game
247/// servers or background applications. When you destroy a graphics context, the engine will remember the options
248/// with which it was created and some of the main window parameters (position, size, etc.) and will re-use these
249/// parameters on a next initialization attempt.
250#[allow(clippy::large_enum_variant)]
251pub enum GraphicsContext {
252    /// Fully initialized graphics context. See [`InitializedGraphicsContext`] docs for more info.
253    Initialized(InitializedGraphicsContext),
254
255    /// Uninitialized (suspended) graphics context. See [`GraphicsContextParams`] docs for more info.
256    Uninitialized(GraphicsContextParams),
257}
258
259impl GraphicsContext {
260    /// Attempts to cast a graphics context to its initialized version. The method will panic if the context
261    /// is not initialized.
262    pub fn as_initialized_ref(&self) -> &InitializedGraphicsContext {
263        if let GraphicsContext::Initialized(ctx) = self {
264            ctx
265        } else {
266            panic!("Graphics context is uninitialized!")
267        }
268    }
269
270    /// Attempts to cast a graphics context to its initialized version. The method will panic if the context
271    /// is not initialized.
272    pub fn as_initialized_mut(&mut self) -> &mut InitializedGraphicsContext {
273        if let GraphicsContext::Initialized(ctx) = self {
274            ctx
275        } else {
276            panic!("Graphics context is uninitialized!")
277        }
278    }
279}
280
281struct SceneLoadingOptions {
282    derived: bool,
283}
284
285/// A helper that is used to load scenes asynchronously.
286///
287/// ## Examples
288///
289/// ```rust
290/// use fyrox_impl::{
291///     core::{color::Color, visitor::prelude::*, reflect::prelude::*, log::Log, pool::Handle},
292///     plugin::{Plugin, PluginContext, error::GameResult},
293///     scene::Scene,
294/// };
295/// use std::path::Path;
296///
297/// #[derive(Visit, Reflect, Debug)]
298/// #[reflect(non_cloneable)]
299/// struct MyGame {
300///     scene: Handle<Scene>,
301/// }
302///
303/// impl MyGame {
304///     pub fn new(scene_path: Option<&str>, context: PluginContext) -> Self {
305///         context
306///             .async_scene_loader
307///             .request(scene_path.unwrap_or("data/scene.rgs"));
308///
309///         Self {
310///             scene: Handle::NONE,
311///         }
312///     }
313/// }
314///
315/// impl Plugin for MyGame {
316///     fn on_scene_begin_loading(&mut self, path: &Path, _context: &mut PluginContext) -> GameResult {
317///         Log::info(format!("{} scene has started loading.", path.display()));
318///
319///         // Use this method if you need to so something when a scene started loading.
320///         Ok(())
321///     }
322///
323///     fn on_scene_loaded(
324///         &mut self,
325///         path: &Path,
326///         scene: Handle<Scene>,
327///         data: &[u8],
328///         context: &mut PluginContext,
329///     ) -> GameResult {
330///         // Optionally remove previous scene.
331///         if self.scene.is_some() {
332///             context.scenes.remove(self.scene);
333///         }
334///
335///         // Remember new scene handle.
336///         self.scene = scene;
337///
338///         Log::info(format!("{} scene was loaded!", path.display()));
339///
340///         // Do something with a newly loaded scene.
341///         let scene_ref = &mut context.scenes[scene];
342///
343///         scene_ref.rendering_options.ambient_lighting_color = Color::opaque(20, 20, 20);
344///
345///         Ok(())
346///     }
347/// }
348/// ```
349///
350/// This example shows a typical usage of the loader, an instance of which is available in the
351/// plugin context. `Game::new` requests a new scene, which internally asks a resource manager to
352/// load the scene. Then, when the scene is fully loaded, the engine calls `Plugin::on_scene_loaded`
353/// method which allows you to do something with the newly loaded scene by taking a reference of it.
354pub struct AsyncSceneLoader {
355    resource_manager: ResourceManager,
356    serialization_context: Arc<SerializationContext>,
357    dyn_type_constructors: Arc<DynTypeConstructorContainer>,
358    receiver: Receiver<SceneLoadingResult>,
359    sender: Sender<SceneLoadingResult>,
360    loading_scenes: FxHashMap<PathBuf, LoadingScene>,
361}
362
363struct LoadingScene {
364    reported: bool,
365    path: PathBuf,
366    options: SceneLoadingOptions,
367}
368
369struct SceneLoadingResult {
370    uuid: Uuid,
371    path: PathBuf,
372    result: Result<(Scene, Vec<u8>), VisitError>,
373}
374
375impl AsyncSceneLoader {
376    fn new(
377        resource_manager: ResourceManager,
378        serialization_context: Arc<SerializationContext>,
379        dyn_type_constructors: Arc<DynTypeConstructorContainer>,
380    ) -> Self {
381        let (sender, receiver) = channel();
382        Self {
383            resource_manager,
384            serialization_context,
385            dyn_type_constructors,
386            receiver,
387            sender,
388            loading_scenes: Default::default(),
389        }
390    }
391
392    fn request_with_options<P: AsRef<Path>>(&mut self, path: P, opts: SceneLoadingOptions) {
393        let path = path.as_ref().to_path_buf();
394
395        if self.loading_scenes.contains_key(&path) {
396            Log::warn(format!("A scene {} is already loading!", path.display()))
397        } else {
398            // Register a new request.
399            self.loading_scenes.insert(
400                path.clone(),
401                LoadingScene {
402                    reported: false,
403                    path: path.clone(),
404                    options: opts,
405                },
406            );
407
408            // Start loading in a separate off-thread task.
409            let sender = self.sender.clone();
410            let serialization_context = self.serialization_context.clone();
411            let dyn_type_constructors = self.dyn_type_constructors.clone();
412            let resource_manager = self.resource_manager.clone();
413            let uuid = resource_manager.find::<Model>(&path).resource_uuid();
414
415            // Acquire the resource IO from the resource manager
416            let io = resource_manager.resource_io();
417            resource_manager.task_pool().spawn_task(async move {
418                match SceneLoader::from_file(
419                    path.clone(),
420                    io.as_ref(),
421                    serialization_context,
422                    dyn_type_constructors,
423                    resource_manager.clone(),
424                )
425                .await
426                {
427                    Ok((loader, data)) => {
428                        let scene = loader.finish().await;
429                        Log::verify(sender.send(SceneLoadingResult {
430                            uuid,
431                            path,
432                            result: Ok((scene, data)),
433                        }));
434                    }
435                    Err(e) => {
436                        Log::verify(sender.send(SceneLoadingResult {
437                            uuid,
438                            path,
439                            result: Err(e),
440                        }));
441                    }
442                }
443            });
444        }
445    }
446
447    /// Requests a scene for loading as derived scene. See [`AsyncSceneLoader`] for usage example.
448    ///
449    /// ## Raw vs Derived Scene
450    ///
451    /// Derived scene means its nodes will derive their properties from the nodes from the source
452    /// scene. Derived scene is useful for saved games - you can serialize your scene as usual and
453    /// it will only contain a "difference" between the original scene and yours. To load the same
454    /// scene as raw scene use [`Self::request_raw`] method.
455    ///
456    /// Raw scene, on other hand, loads the scene as-is without any additional markings for the
457    /// scene nodes. It could be useful to load saved games.
458    pub fn request<P: AsRef<Path>>(&mut self, path: P) {
459        self.request_with_options(path, SceneLoadingOptions { derived: true });
460    }
461
462    /// Requests a scene for loading in raw mode. See [`Self::request`] docs for more info.
463    pub fn request_raw<P: AsRef<Path>>(&mut self, path: P) {
464        self.request_with_options(path, SceneLoadingOptions { derived: false });
465    }
466}
467
468pub(crate) enum GameErrorSource {
469    PluginMethod(&'static str),
470    ScriptMethod {
471        scene_handle: Handle<Scene>,
472        node_handle: Handle<Node>,
473        method_name: &'static str,
474    },
475}
476
477pub(crate) struct GameErrorContainer {
478    source: GameErrorSource,
479    error: GameError,
480}
481
482type ErrorQueue = VecDeque<GameErrorContainer>;
483
484/// See module docs.
485pub struct Engine {
486    /// Graphics context of the engine. See [`GraphicsContext`] docs for more info.
487    pub graphics_context: GraphicsContext,
488
489    /// Current resource manager. Resource manager can be cloned (it does clone only ref) to be able to
490    /// use resource manager from any thread, this is useful to load resources from multiple
491    /// threads to decrease loading times of your game by utilizing all available power of
492    /// your CPU.
493    pub resource_manager: ResourceManager,
494
495    /// All available user interfaces in the engine.
496    pub user_interfaces: UiContainer,
497
498    /// All available scenes in the engine.
499    pub scenes: SceneContainer,
500
501    /// An instance of the async scene loader. See [`AsyncSceneLoader`] docs for usage example.
502    pub async_scene_loader: AsyncSceneLoader,
503
504    /// Task pool for asynchronous task management.
505    pub task_pool: TaskPoolHandler,
506
507    performance_statistics: PerformanceStatistics,
508
509    model_events_receiver: Receiver<ResourceEvent>,
510
511    #[allow(dead_code)] // Keep engine instance alive.
512    sound_engine: SoundEngine,
513
514    // A set of plugins used by the engine.
515    plugins: Vec<PluginContainer>,
516
517    plugins_enabled: bool,
518
519    // Amount of time (in seconds) that passed from creation of the engine.
520    elapsed_time: f32,
521
522    input_state: InputState,
523
524    /// A special container that is able to create nodes by their type UUID. Use a copy of this
525    /// value whenever you need it as a parameter in other parts of the engine.
526    pub serialization_context: Arc<SerializationContext>,
527
528    /// A container with widget constructors.
529    pub widget_constructors: Arc<WidgetConstructorContainer>,
530
531    /// A container with constructors for dynamic types. See [`DynTypeConstructorContainer`] for more
532    /// info.
533    pub dyn_type_constructors: Arc<DynTypeConstructorContainer>,
534
535    /// Script processor is used to run script methods in a strict order.
536    pub script_processor: ScriptProcessor,
537
538    error_queue: ErrorQueue,
539}
540
541#[derive(Debug, Hash, PartialEq, Eq)]
542enum MessageTypeId {
543    Static(TypeId),
544    Dynamic(DynamicTypeId),
545}
546
547/// Performs dispatch of script messages.
548pub struct ScriptMessageDispatcher {
549    type_groups: FxHashMap<MessageTypeId, FxHashSet<Handle<Node>>>,
550    message_receiver: Receiver<ScriptMessage>,
551}
552
553fn try_enqueue_plugin_error(method_name: &'static str, result: GameResult, queue: &mut ErrorQueue) {
554    if let Err(error) = result {
555        queue.push_back(GameErrorContainer {
556            source: GameErrorSource::PluginMethod(method_name),
557            error,
558        });
559    }
560}
561
562impl ScriptMessageDispatcher {
563    fn new(message_receiver: Receiver<ScriptMessage>) -> Self {
564        Self {
565            type_groups: Default::default(),
566            message_receiver,
567        }
568    }
569
570    /// Subscribes a node to receive any message of the given type `T`. Subscription is automatically removed
571    /// if the node dies.
572    pub fn subscribe_to<T: 'static>(&mut self, receiver: Handle<Node>) {
573        self.subscribe_internal_to(receiver, MessageTypeId::Static(TypeId::of::<T>()));
574    }
575
576    /// Subscribes a node to receive any message of the given type `type_id`. Subscription is automatically removed
577    /// if the node dies.
578    pub fn subscribe_dynamic_to(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
579        self.subscribe_internal_to(receiver, MessageTypeId::Dynamic(type_id));
580    }
581
582    #[inline]
583    fn subscribe_internal_to(&mut self, receiver: Handle<Node>, type_id: MessageTypeId) {
584        self.type_groups
585            .entry(type_id)
586            .and_modify(|v| {
587                v.insert(receiver);
588            })
589            .or_insert_with(|| FxHashSet::from_iter([receiver]));
590    }
591
592    /// Unsubscribes a node from receiving any messages of the given type `T`.
593    pub fn unsubscribe_from<T: 'static>(&mut self, receiver: Handle<Node>) {
594        self.unsubscribe_internal_from(receiver, &MessageTypeId::Static(TypeId::of::<T>()));
595    }
596
597    /// Unsubscribes a node from receiving any messages of the given type `type_id`.
598    pub fn unsubscribe_dynamic_from(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
599        self.unsubscribe_internal_from(receiver, &MessageTypeId::Dynamic(type_id));
600    }
601
602    #[inline]
603    fn unsubscribe_internal_from(&mut self, receiver: Handle<Node>, type_id: &MessageTypeId) {
604        if let Some(group) = self.type_groups.get_mut(type_id) {
605            group.remove(&receiver);
606        }
607    }
608
609    /// Unsubscribes a node from receiving any messages.
610    pub fn unsubscribe(&mut self, receiver: Handle<Node>) {
611        for group in self.type_groups.values_mut() {
612            group.remove(&receiver);
613        }
614    }
615
616    fn dispatch_messages(
617        &self,
618        scene: &mut Scene,
619        scene_handle: Handle<Scene>,
620        plugins: &mut [PluginContainer],
621        resource_manager: &ResourceManager,
622        dt: f32,
623        elapsed_time: f32,
624        message_sender: &ScriptMessageSender,
625        user_interfaces: &mut UiContainer,
626        graphics_context: &mut GraphicsContext,
627        task_pool: &mut TaskPoolHandler,
628        input_state: &InputState,
629        error_queue: &mut ErrorQueue,
630    ) {
631        while let Ok(message) = self.message_receiver.try_recv() {
632            let type_id = match message.payload.get_dynamic_type_id() {
633                Some(it) => MessageTypeId::Dynamic(it),
634                None => MessageTypeId::Static(message.payload.deref().type_id()),
635            };
636            let receivers = self.type_groups.get(&type_id);
637
638            if receivers.is_none_or(|r| r.is_empty()) {
639                Log::warn(format!(
640                    "Script message {message:?} was sent, but there's no receivers. \
641                    Did you forgot to subscribe your script to the message?"
642                ));
643            }
644
645            if let Some(receivers) = receivers {
646                let mut payload = message.payload;
647
648                match message.kind {
649                    ScriptMessageKind::Targeted(target) => {
650                        if receivers.contains(&target) {
651                            let mut context = ScriptMessageContext {
652                                dt,
653                                elapsed_time,
654                                plugins: PluginsRefMut(plugins),
655                                handle: target,
656                                scene,
657                                scene_handle,
658                                resource_manager,
659                                message_sender,
660                                task_pool,
661                                graphics_context,
662                                user_interfaces,
663                                script_index: 0,
664                                input_state,
665                            };
666
667                            process_node_scripts(
668                                "on_message",
669                                &mut context,
670                                scene_handle,
671                                error_queue,
672                                &mut |s, ctx| s.on_message(&mut *payload, ctx),
673                            )
674                        }
675                    }
676                    ScriptMessageKind::Hierarchical { root, routing } => match routing {
677                        RoutingStrategy::Up => {
678                            let mut node = root;
679                            while let Ok(node_ref) = scene.graph.try_get_node(node) {
680                                let parent = node_ref.parent();
681
682                                let mut context = ScriptMessageContext {
683                                    dt,
684                                    elapsed_time,
685                                    plugins: PluginsRefMut(plugins),
686                                    handle: node,
687                                    scene,
688                                    scene_handle,
689                                    resource_manager,
690                                    message_sender,
691                                    task_pool,
692                                    graphics_context,
693                                    user_interfaces,
694                                    script_index: 0,
695                                    input_state,
696                                };
697
698                                if receivers.contains(&node) {
699                                    process_node_scripts(
700                                        "on_message",
701                                        &mut context,
702                                        scene_handle,
703                                        error_queue,
704                                        &mut |s, ctx| s.on_message(&mut *payload, ctx),
705                                    );
706                                }
707
708                                node = parent;
709                            }
710                        }
711                        RoutingStrategy::Down => {
712                            for node in scene.graph.traverse_handle_iter(root).collect::<Vec<_>>() {
713                                let mut context = ScriptMessageContext {
714                                    dt,
715                                    elapsed_time,
716                                    plugins: PluginsRefMut(plugins),
717                                    handle: node,
718                                    scene,
719                                    scene_handle,
720                                    resource_manager,
721                                    message_sender,
722                                    task_pool,
723                                    graphics_context,
724                                    user_interfaces,
725                                    script_index: 0,
726                                    input_state,
727                                };
728
729                                if receivers.contains(&node) {
730                                    process_node_scripts(
731                                        "on_message",
732                                        &mut context,
733                                        scene_handle,
734                                        error_queue,
735                                        &mut |s, ctx| s.on_message(&mut *payload, ctx),
736                                    );
737                                }
738                            }
739                        }
740                    },
741                    ScriptMessageKind::Global => {
742                        for &node in receivers {
743                            let mut context = ScriptMessageContext {
744                                dt,
745                                elapsed_time,
746                                plugins: PluginsRefMut(plugins),
747                                handle: node,
748                                scene,
749                                scene_handle,
750                                resource_manager,
751                                message_sender,
752                                task_pool,
753                                graphics_context,
754                                user_interfaces,
755                                script_index: 0,
756                                input_state,
757                            };
758
759                            process_node_scripts(
760                                "on_message",
761                                &mut context,
762                                scene_handle,
763                                error_queue,
764                                &mut |s, ctx| s.on_message(&mut *payload, ctx),
765                            );
766                        }
767                    }
768                }
769            }
770        }
771    }
772}
773
774/// Scripted scene is a handle to scene with some additional data associated with it.
775pub struct ScriptedScene {
776    /// Handle of a scene.
777    pub handle: Handle<Scene>,
778    /// Script message sender.
779    pub message_sender: ScriptMessageSender,
780    message_dispatcher: ScriptMessageDispatcher,
781}
782
783/// Script processor is used to run script methods in a strict order.
784#[derive(Default)]
785pub struct ScriptProcessor {
786    wait_list: Vec<ResourceWaitContext>,
787    /// A list of scenes.
788    pub scripted_scenes: Vec<ScriptedScene>,
789}
790
791impl ScriptProcessor {
792    fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
793        self.scripted_scenes.iter().any(|s| s.handle == scene)
794    }
795
796    fn register_scripted_scene(
797        &mut self,
798        scene: Handle<Scene>,
799        resource_manager: &ResourceManager,
800    ) {
801        // Ensure that the scene wasn't registered previously.
802        assert!(!self.has_scripted_scene(scene));
803
804        let (tx, rx) = channel();
805        self.scripted_scenes.push(ScriptedScene {
806            handle: scene,
807            message_sender: ScriptMessageSender { sender: tx },
808            message_dispatcher: ScriptMessageDispatcher::new(rx),
809        });
810
811        self.wait_list
812            .push(resource_manager.state().get_wait_context());
813    }
814
815    fn handle_scripts(
816        &mut self,
817        scenes: &mut SceneContainer,
818        plugins: &mut [PluginContainer],
819        resource_manager: &ResourceManager,
820        task_pool: &mut TaskPoolHandler,
821        graphics_context: &mut GraphicsContext,
822        user_interfaces: &mut UiContainer,
823        dt: f32,
824        elapsed_time: f32,
825        input_state: &InputState,
826        error_queue: &mut ErrorQueue,
827    ) {
828        self.wait_list
829            .retain_mut(|context| !context.is_all_loaded());
830
831        if !self.wait_list.is_empty() {
832            return;
833        }
834
835        self.scripted_scenes
836            .retain(|s| scenes.is_valid_handle(s.handle));
837
838        'scene_loop: for scripted_scene in self.scripted_scenes.iter_mut() {
839            let scene = &mut scenes[scripted_scene.handle];
840
841            // Disabled scenes should not update their scripts.
842            if !*scene.enabled {
843                continue 'scene_loop;
844            }
845
846            // Fill in initial handles to nodes to initialize, start, update.
847            let mut update_queue = VecDeque::new();
848            let mut start_queue = VecDeque::new();
849            let script_message_sender = scene.graph.script_message_sender.clone();
850            for (handle, node) in scene.graph.pair_iter_mut() {
851                // Remove unused script entries.
852                node.scripts
853                    .retain(|e| e.script.is_some() && !e.should_be_deleted);
854
855                if node.is_globally_enabled() {
856                    for (i, entry) in node.scripts.iter().enumerate() {
857                        if let Some(script) = entry.script.as_ref() {
858                            if script.initialized {
859                                if script.started {
860                                    update_queue.push_back((handle, i));
861                                } else {
862                                    start_queue.push_back((handle, i));
863                                }
864                            } else {
865                                script_message_sender
866                                    .send(NodeScriptMessage::InitializeScript {
867                                        handle,
868                                        script_index: i,
869                                    })
870                                    .unwrap();
871                            }
872                        }
873                    }
874                }
875            }
876
877            // We'll gather all scripts queued for destruction and destroy them all at once at the
878            // end of the frame.
879            let mut destruction_queue = VecDeque::new();
880
881            let max_iterations = 64;
882
883            'update_loop: for update_loop_iteration in 0..max_iterations {
884                let mut context = ScriptContext {
885                    dt,
886                    elapsed_time,
887                    plugins: PluginsRefMut(plugins),
888                    handle: Default::default(),
889                    scene,
890                    scene_handle: scripted_scene.handle,
891                    resource_manager,
892                    message_sender: &scripted_scene.message_sender,
893                    message_dispatcher: &mut scripted_scene.message_dispatcher,
894                    task_pool,
895                    graphics_context,
896                    user_interfaces,
897                    script_index: 0,
898                    input_state,
899                };
900
901                'init_loop: for init_loop_iteration in 0..max_iterations {
902                    // Process events first. `on_init` of a script can also create some other instances
903                    // and these will be correctly initialized on current frame.
904                    while let Ok(event) = context.scene.graph.script_message_receiver.try_recv() {
905                        match event {
906                            NodeScriptMessage::InitializeScript {
907                                handle,
908                                script_index,
909                            } => {
910                                context.handle = handle;
911                                context.script_index = script_index;
912
913                                process_node_script(
914                                    "on_init",
915                                    script_index,
916                                    scripted_scene.handle,
917                                    &mut context,
918                                    error_queue,
919                                    &mut |script, context| {
920                                        if !script.initialized {
921                                            script.on_init(context)?;
922                                            script.initialized = true;
923                                        }
924
925                                        // `on_start` must be called even if the script was initialized.
926                                        start_queue.push_back((handle, script_index));
927
928                                        Ok(())
929                                    },
930                                );
931                            }
932                            NodeScriptMessage::DestroyScript {
933                                handle,
934                                script,
935                                script_index,
936                            } => {
937                                // Destruction is delayed to the end of the frame.
938                                destruction_queue.push_back((handle, script, script_index));
939                            }
940                        }
941                    }
942
943                    if start_queue.is_empty() {
944                        // There is no more new nodes, we can safely leave the init loop.
945                        break 'init_loop;
946                    } else {
947                        // Call `on_start` for every recently initialized node and go to next
948                        // iteration of init loop. This is needed because `on_start` can spawn
949                        // some other nodes that must be initialized before update.
950                        while let Some((handle, script_index)) = start_queue.pop_front() {
951                            context.handle = handle;
952                            context.script_index = script_index;
953
954                            process_node_script(
955                                "on_start",
956                                script_index,
957                                scripted_scene.handle,
958                                &mut context,
959                                error_queue,
960                                &mut |script, context| {
961                                    if script.initialized && !script.started {
962                                        let result = script.on_start(context);
963                                        script.started = true;
964                                        update_queue.push_back((handle, script_index));
965                                        result
966                                    } else {
967                                        Ok(())
968                                    }
969                                },
970                            );
971                        }
972                    }
973
974                    if init_loop_iteration == max_iterations - 1 {
975                        Log::warn(
976                            "Infinite init loop detected! Most likely some of \
977                    your scripts causing infinite prefab instantiation!",
978                        )
979                    }
980                }
981
982                // Update all initialized and started scripts until there is something to initialize.
983                if update_queue.is_empty() {
984                    break 'update_loop;
985                } else {
986                    while let Some((handle, script_index)) = update_queue.pop_front() {
987                        context.handle = handle;
988                        context.script_index = script_index;
989
990                        process_node_script(
991                            "on_update",
992                            script_index,
993                            scripted_scene.handle,
994                            &mut context,
995                            error_queue,
996                            &mut |script, context| script.on_update(context),
997                        );
998                    }
999                }
1000
1001                if update_loop_iteration == max_iterations - 1 {
1002                    Log::warn(
1003                        "Infinite update loop detected! Most likely some of \
1004                    your scripts causing infinite prefab instantiation!",
1005                    )
1006                }
1007            }
1008
1009            // Dispatch script messages only when everything is initialized and updated. This has to
1010            // be done this way, because all those methods could spawn new messages. However, if a new
1011            // message is spawned directly in `on_message` the dispatcher will correctly handle it
1012            // on this frame, since it will be placed in the common queue anyway.
1013            scripted_scene.message_dispatcher.dispatch_messages(
1014                scene,
1015                scripted_scene.handle,
1016                plugins,
1017                resource_manager,
1018                dt,
1019                elapsed_time,
1020                &scripted_scene.message_sender,
1021                user_interfaces,
1022                graphics_context,
1023                task_pool,
1024                input_state,
1025                error_queue,
1026            );
1027
1028            // As the last step, destroy queued scripts.
1029            let mut context = ScriptDeinitContext {
1030                elapsed_time,
1031                plugins: PluginsRefMut(plugins),
1032                resource_manager,
1033                scene,
1034                scene_handle: scripted_scene.handle,
1035                node_handle: Default::default(),
1036                message_sender: &scripted_scene.message_sender,
1037                user_interfaces,
1038                graphics_context,
1039                task_pool,
1040                script_index: 0,
1041                input_state,
1042            };
1043            while let Some((handle, mut script, index)) = destruction_queue.pop_front() {
1044                context.node_handle = handle;
1045                context.script_index = index;
1046
1047                // Unregister self in message dispatcher.
1048                scripted_scene.message_dispatcher.unsubscribe(handle);
1049
1050                // `on_deinit` could also spawn new nodes, but we won't take those into account on
1051                // this frame. They'll be correctly handled on next frame.
1052                if let Err(error) = script.on_deinit(&mut context) {
1053                    error_queue.push_back(GameErrorContainer {
1054                        source: GameErrorSource::ScriptMethod {
1055                            scene_handle: scripted_scene.handle,
1056                            node_handle: handle,
1057                            method_name: "on_deinit",
1058                        },
1059                        error,
1060                    });
1061                }
1062            }
1063        }
1064
1065        // Process scripts from destroyed scenes.
1066        for (handle, mut detached_scene) in scenes.destruction_list.drain(..) {
1067            if let Some(scripted_scene) = self.scripted_scenes.iter().find(|s| s.handle == handle) {
1068                let mut context = ScriptDeinitContext {
1069                    elapsed_time,
1070                    plugins: PluginsRefMut(plugins),
1071                    resource_manager,
1072                    scene: &mut detached_scene,
1073                    scene_handle: scripted_scene.handle,
1074                    node_handle: Default::default(),
1075                    message_sender: &scripted_scene.message_sender,
1076                    task_pool,
1077                    graphics_context,
1078                    user_interfaces,
1079                    script_index: 0,
1080                    input_state,
1081                };
1082
1083                // Destroy every script instance from nodes that were still alive.
1084                for node_index in 0..context.scene.graph.capacity() {
1085                    let handle_node = context.scene.graph.handle_from_index(node_index);
1086                    context.node_handle = handle_node;
1087
1088                    process_node_scripts(
1089                        "on_deinit",
1090                        &mut context,
1091                        scripted_scene.handle,
1092                        error_queue,
1093                        &mut |script, context| {
1094                            if script.initialized {
1095                                script.on_deinit(context)
1096                            } else {
1097                                Ok(())
1098                            }
1099                        },
1100                    );
1101                }
1102            }
1103        }
1104    }
1105}
1106
1107struct ResourceGraphVertex {
1108    resource: ModelResource,
1109    children: Vec<ResourceGraphVertex>,
1110}
1111
1112impl ResourceGraphVertex {
1113    pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1114        let mut children = Vec::new();
1115
1116        // Look for dependent resources.
1117        let mut dependent_resources = HashSet::new();
1118        for resource in resource_manager.state().iter() {
1119            if let Some(other_model) = resource.try_cast::<Model>() {
1120                let mut state = other_model.state();
1121                if let Some(model_data) = state.data() {
1122                    if model_data
1123                        .get_scene()
1124                        .graph
1125                        .linear_iter()
1126                        .any(|n| n.resource.as_ref() == Some(&model))
1127                    {
1128                        dependent_resources.insert(other_model.clone());
1129                    }
1130                }
1131            }
1132        }
1133
1134        children.extend(
1135            dependent_resources
1136                .into_iter()
1137                .map(|r| ResourceGraphVertex::new(r, resource_manager.clone())),
1138        );
1139
1140        Self {
1141            resource: model,
1142            children,
1143        }
1144    }
1145
1146    pub fn resolve(&self) {
1147        Log::info(format!(
1148            "Resolving {} resource from dependency graph...",
1149            self.resource.resource_uuid()
1150        ));
1151
1152        // Wait until resource is fully loaded, then resolve.
1153        if block_on(self.resource.clone()).is_ok() {
1154            self.resource.data_ref().get_scene_mut();
1155
1156            for child in self.children.iter() {
1157                child.resolve();
1158            }
1159        }
1160    }
1161}
1162
1163struct ResourceDependencyGraph {
1164    root: ResourceGraphVertex,
1165}
1166
1167impl ResourceDependencyGraph {
1168    pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1169        Self {
1170            root: ResourceGraphVertex::new(model, resource_manager),
1171        }
1172    }
1173
1174    pub fn resolve(&self) {
1175        self.root.resolve()
1176    }
1177}
1178
1179/// A result returned by a graphics server constructor.
1180pub type GraphicsServerConstructorResult = Result<(Window, SharedGraphicsServer), FrameworkError>;
1181
1182/// Graphics server constructor callback responsible for actual server creation. Graphics server
1183/// initialization usually tied together with window creation on some operating systems, that's why
1184/// the constructor accepts window builder and must return the window built with the builder as well
1185/// as the server.
1186pub type GraphicsServerConstructorCallback = dyn Fn(
1187    &GraphicsContextParams,
1188    &ActiveEventLoop,
1189    WindowAttributes,
1190    bool,
1191) -> GraphicsServerConstructorResult;
1192
1193/// Graphics server constructor is used to initialize different graphics servers in unified manner.
1194/// [`Default`] trait implementation currently creates OpenGL graphics server.
1195#[derive(Clone)]
1196pub struct GraphicsServerConstructor(Rc<GraphicsServerConstructorCallback>);
1197
1198impl Default for GraphicsServerConstructor {
1199    fn default() -> Self {
1200        Self(Rc::new(
1201            |params, window_target, window_builder, named_objects| {
1202                GlGraphicsServer::new(
1203                    params.vsync,
1204                    params.msaa_sample_count,
1205                    window_target,
1206                    window_builder,
1207                    named_objects,
1208                )
1209            },
1210        ))
1211    }
1212}
1213
1214/// A set of parameters that could be used to initialize graphics context.
1215#[derive(Clone)]
1216pub struct GraphicsContextParams {
1217    /// Attributes of the main application window.
1218    pub window_attributes: WindowAttributes,
1219
1220    /// Whether to use vertical synchronization or not. V-sync will force your game to render frames with the synchronization
1221    /// rate of your monitor (which is ~60 FPS). Keep in mind that vertical synchronization might not be available on your OS.
1222    pub vsync: bool,
1223
1224    /// Amount of samples for MSAA. Must be a power of two (1, 2, 4, 8). `None` means disabled.
1225    /// MSAA works only for forward rendering and does not work for deferred rendering.
1226    pub msaa_sample_count: Option<u8>,
1227
1228    /// Graphic server constructor. See [`GraphicsServerConstructor`] docs for more info.
1229    pub graphics_server_constructor: GraphicsServerConstructor,
1230
1231    /// A flag, that if raised tells the engine to assign meaningful names for GPU objects. This
1232    /// option is very useful for debugging. This option is off by default, because if may cause
1233    /// crashes on some video driver due to poor implementation in the driver.
1234    pub named_objects: bool,
1235}
1236
1237impl Default for GraphicsContextParams {
1238    fn default() -> Self {
1239        Self {
1240            window_attributes: Default::default(),
1241            vsync: true,
1242            msaa_sample_count: None,
1243            graphics_server_constructor: Default::default(),
1244            named_objects: false,
1245        }
1246    }
1247}
1248
1249/// Engine initialization parameters.
1250pub struct EngineInitParams {
1251    /// A set of parameters for graphics context initialization. Keep in mind that the engine **will not** initialize
1252    /// graphics context for you. Instead, you need to call [`Engine::initialize_graphics_context`] on [`Event::Resumed`]
1253    /// event and [`Engine::destroy_graphics_context`] on [`Event::Suspended`] event. If you don't need a graphics context
1254    /// (for example for game servers), then you can pass [`Default::default`] here and do not call any methods.
1255    pub graphics_context_params: GraphicsContextParams,
1256    /// A special container that is able to create nodes by their type UUID.
1257    pub serialization_context: Arc<SerializationContext>,
1258    /// A container with widget constructors.
1259    pub widget_constructors: Arc<WidgetConstructorContainer>,
1260    /// A container for dynamic types. See [`DynTypeConstructorContainer`] docs for more info.
1261    pub dyn_type_constructors: Arc<DynTypeConstructorContainer>,
1262    /// A resource manager.
1263    pub resource_manager: ResourceManager,
1264    /// Task pool for asynchronous task management.
1265    pub task_pool: Arc<TaskPool>,
1266}
1267
1268fn process_node_script<T, C>(
1269    caller_name: &'static str,
1270    index: usize,
1271    scene_handle: Handle<Scene>,
1272    context: &mut C,
1273    error_queue: &mut ErrorQueue,
1274    func: &mut T,
1275) -> bool
1276where
1277    T: FnMut(&mut Script, &mut C) -> GameResult,
1278    C: UniversalScriptContext,
1279{
1280    let Ok(node) = context.node() else {
1281        // A node was destroyed.
1282        return false;
1283    };
1284
1285    if !node.is_globally_enabled() {
1286        return false;
1287    }
1288
1289    let Some(entry) = node.scripts.get_mut(index) else {
1290        // All scripts were visited.
1291        return false;
1292    };
1293
1294    let Some(mut script) = entry.take() else {
1295        return false;
1296    };
1297
1298    if let Err(error) = func(&mut script, context) {
1299        let node = context.node();
1300        let handle = node.map_or(Handle::NONE, |n| n.handle());
1301        error_queue.push_back(GameErrorContainer {
1302            source: GameErrorSource::ScriptMethod {
1303                scene_handle,
1304                node_handle: handle,
1305                method_name: caller_name,
1306            },
1307            error,
1308        });
1309    }
1310
1311    match context.node() {
1312        Ok(node) => {
1313            let entry = node
1314                .scripts
1315                .get_mut(index)
1316                .expect("Scripts array cannot be modified!");
1317
1318            if entry.should_be_deleted {
1319                context.destroy_script_deferred(script, index);
1320            } else {
1321                // Put the script back at its place.
1322                entry.script = Some(script);
1323            }
1324        }
1325        Err(_) => {
1326            // If the node was deleted by the `func` call, we must send the script to destruction
1327            // queue, not silently drop it.
1328            context.destroy_script_deferred(script, index);
1329        }
1330    }
1331
1332    true
1333}
1334
1335fn process_node_scripts<T, C>(
1336    caller_name: &'static str,
1337    context: &mut C,
1338    scene_handle: Handle<Scene>,
1339    error_queue: &mut ErrorQueue,
1340    func: &mut T,
1341) where
1342    T: FnMut(&mut Script, &mut C) -> GameResult,
1343    C: UniversalScriptContext,
1344{
1345    let mut index = 0;
1346    loop {
1347        context.set_script_index(index);
1348
1349        if !process_node_script(caller_name, index, scene_handle, context, error_queue, func) {
1350            return;
1351        }
1352
1353        // Advance to the next script.
1354        index += 1;
1355    }
1356}
1357
1358pub(crate) fn process_scripts<T>(
1359    caller_name: &'static str,
1360    scene: &mut Scene,
1361    scene_handle: Handle<Scene>,
1362    plugins: &mut [PluginContainer],
1363    resource_manager: &ResourceManager,
1364    message_sender: &ScriptMessageSender,
1365    message_dispatcher: &mut ScriptMessageDispatcher,
1366    task_pool: &mut TaskPoolHandler,
1367    graphics_context: &mut GraphicsContext,
1368    user_interfaces: &mut UiContainer,
1369    dt: f32,
1370    elapsed_time: f32,
1371    input_state: &InputState,
1372    error_queue: &mut ErrorQueue,
1373    mut func: T,
1374) where
1375    T: FnMut(&mut Script, &mut ScriptContext) -> GameResult,
1376{
1377    let mut context = ScriptContext {
1378        dt,
1379        elapsed_time,
1380        plugins: PluginsRefMut(plugins),
1381        handle: Default::default(),
1382        scene,
1383        scene_handle,
1384        resource_manager,
1385        message_sender,
1386        message_dispatcher,
1387        task_pool,
1388        graphics_context,
1389        user_interfaces,
1390        script_index: 0,
1391        input_state,
1392    };
1393
1394    for node_index in 0..context.scene.graph.capacity() {
1395        context.handle = context.scene.graph.handle_from_index(node_index);
1396
1397        process_node_scripts(
1398            caller_name,
1399            &mut context,
1400            scene_handle,
1401            error_queue,
1402            &mut func,
1403        );
1404    }
1405}
1406
1407pub(crate) fn initialize_resource_manager_loaders(
1408    resource_manager: &ResourceManager,
1409    serialization_context: Arc<SerializationContext>,
1410    widget_constructors: Arc<WidgetConstructorContainer>,
1411    dyn_type_constructors: Arc<DynTypeConstructorContainer>,
1412) {
1413    let model_loader = ModelLoader {
1414        resource_manager: resource_manager.clone(),
1415        serialization_context,
1416        dyn_type_constructors: dyn_type_constructors.clone(),
1417        default_import_options: Default::default(),
1418    };
1419
1420    let mut state = resource_manager.state();
1421
1422    for shader in ShaderResource::standard_shaders() {
1423        state.built_in_resources.add((*shader).clone());
1424    }
1425    state.built_in_resources.add(GLTF_SHADER.clone());
1426
1427    for texture in SkyBoxKind::built_in_skybox_textures() {
1428        state.built_in_resources.add(texture.clone());
1429    }
1430
1431    state.built_in_resources.add(BUILT_IN_FONT.clone());
1432    state.built_in_resources.add(BUILT_IN_BOLD.clone());
1433    state.built_in_resources.add(BUILT_IN_ITALIC.clone());
1434    state.built_in_resources.add(BOLD_ITALIC.clone());
1435
1436    state.built_in_resources.add(texture::PLACEHOLDER.clone());
1437    state.built_in_resources.add(texture::PURE_COLOR.clone());
1438    state.built_in_resources.add(style::DEFAULT_STYLE.clone());
1439
1440    for material in [
1441        &*material::STANDARD,
1442        &*material::STANDARD_2D,
1443        &*material::STANDARD_SPRITE,
1444        &*material::STANDARD_TERRAIN,
1445        &*material::STANDARD_TWOSIDES,
1446        &*material::STANDARD_PARTICLE_SYSTEM,
1447        &*material::STANDARD_WIDGET,
1448    ] {
1449        state.built_in_resources.add(material.clone());
1450    }
1451
1452    for surface in [
1453        &*surface::CUBE,
1454        &*surface::QUAD,
1455        &*surface::CYLINDER,
1456        &*surface::SPHERE,
1457        &*surface::CONE,
1458        &*surface::TORUS,
1459    ] {
1460        state.built_in_resources.add(surface.clone());
1461    }
1462
1463    state.constructors_container.add::<Texture>();
1464    state.constructors_container.add::<Shader>();
1465    state.constructors_container.add::<Model>();
1466    state.constructors_container.add::<CurveResourceState>();
1467    state.constructors_container.add::<SoundBuffer>();
1468    state.constructors_container.add::<HrirSphereResourceData>();
1469    state.constructors_container.add::<Material>();
1470    state.constructors_container.add::<Font>();
1471    state.constructors_container.add::<UserInterface>();
1472    state.constructors_container.add::<SurfaceData>();
1473    state.constructors_container.add::<TileSet>();
1474    state.constructors_container.add::<TileMapBrush>();
1475    state.constructors_container.add::<TileMapData>();
1476    state.constructors_container.add::<CustomTileCollider>();
1477    state.constructors_container.add::<AnimationTracksData>();
1478    state.constructors_container.add::<Style>();
1479
1480    let mut loaders = state.loaders.safe_lock();
1481    let gltf_loader = super::resource::gltf::GltfLoader {
1482        resource_manager: resource_manager.clone(),
1483        default_import_options: Default::default(),
1484    };
1485    loaders.set(gltf_loader);
1486    loaders.set(model_loader);
1487    loaders.set(TextureLoader {
1488        default_import_options: Default::default(),
1489    });
1490    loaders.set(SoundBufferLoader {
1491        default_import_options: Default::default(),
1492    });
1493    loaders.set(ShaderLoader);
1494    loaders.set(CurveLoader);
1495    loaders.set(HrirSphereLoader);
1496    loaders.set(MaterialLoader {
1497        resource_manager: resource_manager.clone(),
1498    });
1499    loaders.set(FontLoader::new(resource_manager.clone()));
1500    loaders.set(UserInterfaceLoader {
1501        resource_manager: resource_manager.clone(),
1502        constructors: widget_constructors,
1503        dyn_type_constructors,
1504    });
1505    loaders.set(SurfaceDataLoader {});
1506    loaders.set(TileSetLoader {
1507        resource_manager: resource_manager.clone(),
1508    });
1509    loaders.set(TileMapBrushLoader {
1510        resource_manager: resource_manager.clone(),
1511    });
1512    loaders.set(StyleLoader {
1513        resource_manager: resource_manager.clone(),
1514    });
1515}
1516
1517/// A controller for the application loop.
1518#[derive(Copy, Clone)]
1519pub enum ApplicationLoopController<'a> {
1520    /// Headless loop controller. This variant is used when the engine is running in headless mode
1521    /// (with no window, graphics, sound, etc. support), usually used by game servers.
1522    Headless {
1523        /// A variable that controls execution of the game loop. If set to `false` the loop will
1524        /// exit and the application will end its execution.
1525        running: &'a Cell<bool>,
1526    },
1527    /// Normal application loop controller with full window, graphics, sound support.
1528    ActiveEventLoop(&'a ActiveEventLoop),
1529    /// Normal application loop controller without a window.
1530    EventLoop(&'a EventLoop<()>),
1531}
1532
1533impl ApplicationLoopController<'_> {
1534    /// Asks the loop controller to end the application execution.
1535    pub fn exit(&self) {
1536        match self {
1537            ApplicationLoopController::Headless { running } => running.set(false),
1538            ApplicationLoopController::ActiveEventLoop(event_loop) => event_loop.exit(),
1539            ApplicationLoopController::EventLoop(_) => {
1540                warn!("Can't exit the loop until it is activated!")
1541            }
1542        }
1543    }
1544
1545    /// Returns `true` if the loop will be destroyed in the next frame and the application will
1546    /// shut down.
1547    pub fn exiting(&self) -> bool {
1548        match self {
1549            ApplicationLoopController::Headless { running } => !running.get(),
1550            ApplicationLoopController::ActiveEventLoop(event_loop) => event_loop.exiting(),
1551            ApplicationLoopController::EventLoop(_) => false,
1552        }
1553    }
1554}
1555
1556impl Engine {
1557    /// Creates new instance of engine from given initialization parameters. Automatically creates all sub-systems
1558    /// (sound, ui, resource manager, etc.) **except** graphics context. Graphics context should be created manually
1559    /// only on [`Event::Resumed`] by calling [`Engine::initialize_graphics_context`] and destroyed on [`Event::Suspended`]
1560    /// by calling [`Engine::destroy_graphics_context`]. If you don't need a graphics context (for example if you're
1561    /// making a game server), then you can ignore these methods.
1562    ///
1563    /// # Examples
1564    ///
1565    /// ```no_run
1566    /// # use fyrox_impl::{
1567    /// #     asset::manager::ResourceManager,
1568    /// #     engine::{
1569    /// #         Engine, EngineInitParams, GraphicsContextParams,
1570    /// #         SerializationContext,
1571    /// #     },
1572    /// #     event_loop::EventLoop,
1573    /// #     window::WindowAttributes,
1574    /// # };
1575    /// # use std::sync::Arc;
1576    /// use fyrox_core::dyntype::DynTypeConstructorContainer;
1577    /// # use fyrox_core::task::TaskPool;
1578    /// # use fyrox_resource::io::FsResourceIo;
1579    /// # use fyrox_ui::constructor::new_widget_constructor_container;
1580    ///
1581    /// let mut window_attributes = WindowAttributes::default();
1582    /// window_attributes.title = "Some title".to_string();
1583    /// let graphics_context_params = GraphicsContextParams {
1584    ///     window_attributes,
1585    ///     vsync: true,
1586    ///     msaa_sample_count: None,
1587    ///     graphics_server_constructor: Default::default(),
1588    ///     named_objects: false
1589    /// };
1590    /// let task_pool = Arc::new(TaskPool::new());
1591    ///
1592    /// Engine::new(EngineInitParams {
1593    ///     graphics_context_params,
1594    ///     resource_manager: ResourceManager::new(Arc::new(FsResourceIo), task_pool.clone()),
1595    ///     dyn_type_constructors: Arc::new(DynTypeConstructorContainer::default()),
1596    ///     serialization_context: Arc::new(SerializationContext::new()),
1597    ///     task_pool,
1598    ///     widget_constructors: Arc::new(new_widget_constructor_container()),
1599    /// })
1600    /// .unwrap();
1601    /// ```
1602    #[inline]
1603    #[allow(unused_variables)]
1604    pub fn new(params: EngineInitParams) -> Result<Self, EngineError> {
1605        let EngineInitParams {
1606            graphics_context_params,
1607            serialization_context,
1608            widget_constructors,
1609            dyn_type_constructors,
1610            resource_manager,
1611            task_pool,
1612        } = params;
1613
1614        #[cfg(target_arch = "wasm32")]
1615        wasm_utils::set_panic_hook();
1616
1617        initialize_resource_manager_loaders(
1618            &resource_manager,
1619            serialization_context.clone(),
1620            widget_constructors.clone(),
1621            dyn_type_constructors.clone(),
1622        );
1623
1624        let (rx, tx) = channel();
1625        resource_manager.state().event_broadcaster.add(rx);
1626
1627        let sound_engine = SoundEngine::without_device();
1628
1629        let user_interfaces =
1630            UiContainer::new_with_ui(UserInterface::new(Vector2::new(100.0, 100.0)));
1631
1632        Ok(Self {
1633            graphics_context: GraphicsContext::Uninitialized(graphics_context_params),
1634            model_events_receiver: tx,
1635            async_scene_loader: AsyncSceneLoader::new(
1636                resource_manager.clone(),
1637                serialization_context.clone(),
1638                dyn_type_constructors.clone(),
1639            ),
1640            resource_manager,
1641            scenes: SceneContainer::new(sound_engine.clone()),
1642            sound_engine,
1643            user_interfaces,
1644            performance_statistics: Default::default(),
1645            plugins: Default::default(),
1646            serialization_context,
1647            widget_constructors,
1648            dyn_type_constructors,
1649            script_processor: Default::default(),
1650            plugins_enabled: false,
1651            elapsed_time: 0.0,
1652            task_pool: TaskPoolHandler::new(task_pool),
1653            input_state: Default::default(),
1654            error_queue: Default::default(),
1655        })
1656    }
1657
1658    /// Tries to initialize the graphics context. The method will attempt to use the info stored in `graphics_context`
1659    /// variable of the engine to attempt to initialize the graphics context. It will fail if the graphics context is
1660    /// already initialized as well as if there any platform-dependent error (for example your hardware does not support
1661    /// OpenGL 3.3 Core or OpenGL ES 3.0).
1662    ///
1663    /// This method should be called on [`Event::Resumed`] of your game loop, however you can ignore it if you don't need
1664    /// graphics context at all (for example - if you're making game server).
1665    pub fn initialize_graphics_context(
1666        &mut self,
1667        event_loop: &ActiveEventLoop,
1668    ) -> Result<(), EngineError> {
1669        if let GraphicsContext::Uninitialized(params) = &self.graphics_context {
1670            let (window, server) = params.graphics_server_constructor.0(
1671                params,
1672                event_loop,
1673                params.window_attributes.clone(),
1674                params.named_objects,
1675            )?;
1676            let frame_size = (window.inner_size().width, window.inner_size().height);
1677
1678            let renderer = Renderer::new(server, frame_size, &self.resource_manager)?;
1679
1680            for ui in self.user_interfaces.iter_mut() {
1681                ui.set_screen_size(Vector2::new(frame_size.0 as f32, frame_size.1 as f32));
1682            }
1683
1684            self.graphics_context = GraphicsContext::Initialized(InitializedGraphicsContext {
1685                renderer,
1686                window,
1687                params: params.clone(),
1688            });
1689
1690            if let Err(err) = self.sound_engine.initialize_audio_output_device() {
1691                Log::err(format!(
1692                    "Unable to initialize audio output device! Reason: {err:?}"
1693                ));
1694            }
1695
1696            Ok(())
1697        } else {
1698            Err(EngineError::Custom(
1699                "Graphics context is already initialized!".to_string(),
1700            ))
1701        }
1702    }
1703
1704    /// Tries to destroy current graphics context. It will succeed only if the `graphics_context` is fully initialized.
1705    /// The method will try to save all possible runtime changes of the window, so the next [`Engine::initialize_graphics_context`]
1706    /// will result in the almost exact copy of the context that was made before destruction.
1707    ///
1708    /// This method should be called on [`Event::Suspended`] of your game loop, however if you do not use any graphics context
1709    /// (for example - if you're making a game server), then you can ignore this method completely.
1710    pub fn destroy_graphics_context(&mut self) -> Result<(), EngineError> {
1711        if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
1712            let params = &ctx.params;
1713            let window = &ctx.window;
1714
1715            let mut window_attributes = WindowAttributes::default();
1716
1717            window_attributes.inner_size = Some(Size::Physical(window.inner_size()));
1718            window_attributes.min_inner_size = params.window_attributes.min_inner_size;
1719            window_attributes.max_inner_size = params.window_attributes.max_inner_size;
1720            window_attributes.position = window.outer_position().ok().map(Position::Physical);
1721            window_attributes.resizable = window.is_resizable();
1722            window_attributes.enabled_buttons = window.enabled_buttons();
1723            window_attributes.title = window.title();
1724            window_attributes.maximized = window.is_maximized();
1725            window_attributes.visible = window.is_visible().unwrap_or(true);
1726            window_attributes.transparent = params.window_attributes.transparent;
1727            window_attributes.decorations = window.is_decorated();
1728            window_attributes.preferred_theme = params.window_attributes.preferred_theme;
1729            window_attributes.resize_increments = window.resize_increments().map(Size::Physical);
1730            window_attributes.content_protected = params.window_attributes.content_protected;
1731            window_attributes.window_level = params.window_attributes.window_level;
1732            window_attributes.active = params.window_attributes.active;
1733            window_attributes
1734                .window_icon
1735                .clone_from(&params.window_attributes.window_icon);
1736
1737            self.graphics_context = GraphicsContext::Uninitialized(GraphicsContextParams {
1738                window_attributes,
1739                vsync: params.vsync,
1740                msaa_sample_count: params.msaa_sample_count,
1741                graphics_server_constructor: params.graphics_server_constructor.clone(),
1742                named_objects: params.named_objects,
1743            });
1744
1745            self.sound_engine.destroy_audio_output_device();
1746
1747            Ok(())
1748        } else {
1749            Err(EngineError::Custom(
1750                "Graphics context is already destroyed!".to_string(),
1751            ))
1752        }
1753    }
1754
1755    /// Adjust size of the frame to be rendered. Must be called after the window size changes.
1756    /// Will update the renderer and GL context frame size.
1757    pub fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
1758        if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
1759            ctx.renderer.set_frame_size(new_size)?;
1760        }
1761
1762        Ok(())
1763    }
1764
1765    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
1766    /// this value is **not** guaranteed to match real time. A user can change delta time with
1767    /// which the engine "ticks" and this delta time affects elapsed time.
1768    pub fn elapsed_time(&self) -> f32 {
1769        self.elapsed_time
1770    }
1771
1772    /// Performs single update tick with given time delta. Engine internally will perform update
1773    /// of all scenes, sub-systems, user interface, etc. Must be called in order to get engine
1774    /// functioning.
1775    ///
1776    /// ## Parameters
1777    ///
1778    /// `lag` - is a reference to time accumulator, that holds remaining amount of time that should be used
1779    /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus stabilizes
1780    /// update rate. The main use of this variable, is to be able to reset `lag` when you doing some heavy
1781    /// calculations in a your game loop (i.e. loading a new level) so the engine won't try to "catch up" with
1782    /// all the time that was spent in heavy calculation. The engine does **not** use this variable itself,
1783    /// but the plugins attach may use it, that's why you need to provide it. If you don't use plugins, then
1784    /// put `&mut 0.0` here.
1785    pub fn update(
1786        &mut self,
1787        dt: f32,
1788        controller: ApplicationLoopController,
1789        lag: &mut f32,
1790        switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
1791    ) {
1792        self.handle_async_scene_loading(dt, lag, controller);
1793        self.pre_update(dt, controller, lag, switches);
1794        self.post_update(dt, &Default::default(), lag, controller);
1795        self.handle_plugins_hot_reloading(dt, controller, lag, |_| {});
1796    }
1797
1798    /// Tries to hot-reload dynamic plugins marked for reloading.
1799    ///
1800    /// ## Platform-specific
1801    ///
1802    /// - Windows, Unix-like systems (Linux, macOS, FreeBSD, etc) - fully supported.
1803    /// - WebAssembly - not supported
1804    /// - Android - not supported
1805    pub fn handle_plugins_hot_reloading<F>(
1806        &mut self,
1807        #[allow(unused_variables)] dt: f32,
1808        #[allow(unused_variables)] controller: ApplicationLoopController,
1809        #[allow(unused_variables)] lag: &mut f32,
1810        #[allow(unused_variables)] on_reloaded: F,
1811    ) where
1812        F: FnMut(&dyn Plugin),
1813    {
1814        #[cfg(any(unix, windows))]
1815        {
1816            if let Err(message) = self.reload_dynamic_plugins(dt, controller, lag, on_reloaded) {
1817                Log::err(format!(
1818                    "Unable to reload dynamic plugins. Reason: {message}"
1819                ))
1820            }
1821        }
1822    }
1823
1824    fn handle_async_scene_loading(
1825        &mut self,
1826        dt: f32,
1827        lag: &mut f32,
1828        controller: ApplicationLoopController,
1829    ) {
1830        let len = self.async_scene_loader.loading_scenes.len();
1831        let mut n = 0;
1832        while n < len {
1833            if let Some(request) = self.async_scene_loader.loading_scenes.values_mut().nth(n) {
1834                if !request.reported {
1835                    request.reported = true;
1836
1837                    // Notify plugins about a scene, that started loading.
1838                    if self.plugins_enabled {
1839                        let path = request.path.clone();
1840                        let mut context = PluginContext {
1841                            scenes: &mut self.scenes,
1842                            resource_manager: &self.resource_manager,
1843                            graphics_context: &mut self.graphics_context,
1844                            dt,
1845                            lag,
1846                            user_interfaces: &mut self.user_interfaces,
1847                            serialization_context: &self.serialization_context,
1848                            widget_constructors: &self.widget_constructors,
1849                            dyn_type_constructors: &self.dyn_type_constructors,
1850                            performance_statistics: &self.performance_statistics,
1851                            elapsed_time: self.elapsed_time,
1852                            script_processor: &self.script_processor,
1853                            async_scene_loader: &mut self.async_scene_loader,
1854                            loop_controller: controller,
1855                            task_pool: &mut self.task_pool,
1856                            input_state: &self.input_state,
1857                        };
1858
1859                        for plugin in self.plugins.iter_mut() {
1860                            try_enqueue_plugin_error(
1861                                "on_scene_begin_loading",
1862                                plugin.on_scene_begin_loading(&path, &mut context),
1863                                &mut self.error_queue,
1864                            );
1865                        }
1866                    }
1867                }
1868            }
1869
1870            n += 1;
1871        }
1872
1873        while let Ok(loading_result) = self.async_scene_loader.receiver.try_recv() {
1874            if let Some(request) = self
1875                .async_scene_loader
1876                .loading_scenes
1877                .remove(&loading_result.path)
1878            {
1879                let mut context = PluginContext {
1880                    scenes: &mut self.scenes,
1881                    resource_manager: &self.resource_manager,
1882                    graphics_context: &mut self.graphics_context,
1883                    dt,
1884                    lag,
1885                    user_interfaces: &mut self.user_interfaces,
1886                    serialization_context: &self.serialization_context,
1887                    widget_constructors: &self.widget_constructors,
1888                    dyn_type_constructors: &self.dyn_type_constructors,
1889                    performance_statistics: &self.performance_statistics,
1890                    elapsed_time: self.elapsed_time,
1891                    script_processor: &self.script_processor,
1892                    async_scene_loader: &mut self.async_scene_loader,
1893                    loop_controller: controller,
1894                    task_pool: &mut self.task_pool,
1895                    input_state: &self.input_state,
1896                };
1897
1898                match loading_result.result {
1899                    Ok((mut scene, data)) => {
1900                        if request.options.derived {
1901                            let model = self
1902                                .resource_manager
1903                                .find_uuid::<Model>(loading_result.uuid);
1904                            // Create a resource, that will point to the scene we've loaded the
1905                            // scene from and force scene nodes to inherit data from them.
1906                            let data = Model {
1907                                mapping: NodeMapping::UseHandles,
1908                                // We have to create a full copy of the scene, because otherwise
1909                                // some methods (`Base::root_resource` in particular) won't work
1910                                // correctly.
1911                                scene: scene
1912                                    .clone_ex(
1913                                        scene.graph.get_root(),
1914                                        true,
1915                                        &mut |_, _| true,
1916                                        &mut |_, _| {},
1917                                        &mut |_, _, _| {},
1918                                    )
1919                                    .0,
1920                            };
1921                            model.header().state.commit_ok(data);
1922
1923                            for (handle, node) in scene.graph.pair_iter_mut() {
1924                                node.set_inheritance_data(handle, model.clone());
1925                            }
1926
1927                            // Reset modified flags in every inheritable property of the scene.
1928                            // Except nodes, they're inherited in a separate place.
1929                            (&mut scene as &mut dyn Reflect).apply_recursively_mut(
1930                                &mut |object| {
1931                                    let type_id = (*object).type_id();
1932                                    if type_id != TypeId::of::<NodePool>() {
1933                                        object.as_inheritable_variable_mut(&mut |variable| {
1934                                            if let Some(variable) = variable {
1935                                                variable.reset_modified_flag();
1936                                            }
1937                                        });
1938                                    }
1939                                },
1940                                &[
1941                                    TypeId::of::<UntypedResource>(),
1942                                    TypeId::of::<navmesh::Container>(),
1943                                ],
1944                            )
1945                        } else {
1946                            // Take scene data from the source scene.
1947                            if let Some(source_asset) =
1948                                scene.graph[scene.graph.get_root()].root_resource()
1949                            {
1950                                let source_asset_ref = source_asset.data_ref();
1951                                let source_scene_ref = &source_asset_ref.scene;
1952                                Log::verify(try_inherit_properties(
1953                                    &mut scene,
1954                                    source_scene_ref,
1955                                    &[
1956                                        TypeId::of::<NodePool>(),
1957                                        TypeId::of::<UntypedResource>(),
1958                                        TypeId::of::<navmesh::Container>(),
1959                                    ],
1960                                ));
1961                            }
1962                        }
1963
1964                        let scene_handle = context.scenes.add(scene);
1965
1966                        // Notify plugins about newly loaded scene.
1967                        if self.plugins_enabled {
1968                            for plugin in self.plugins.iter_mut() {
1969                                Log::info(format!(
1970                                    "Scene {} was loaded successfully!",
1971                                    loading_result.path.display()
1972                                ));
1973
1974                                try_enqueue_plugin_error(
1975                                    "on_scene_loaded",
1976                                    plugin.on_scene_loaded(
1977                                        &request.path,
1978                                        scene_handle,
1979                                        &data,
1980                                        &mut context,
1981                                    ),
1982                                    &mut self.error_queue,
1983                                );
1984                            }
1985                        }
1986                    }
1987                    Err(error) => {
1988                        // Notify plugins about a scene, that is failed to load.
1989                        if self.plugins_enabled {
1990                            Log::err(format!(
1991                                "Unable to load scene {:?}. Reason: {}",
1992                                loading_result.path, error
1993                            ));
1994
1995                            for plugin in self.plugins.iter_mut() {
1996                                try_enqueue_plugin_error(
1997                                    "on_scene_loading_failed",
1998                                    plugin.on_scene_loading_failed(
1999                                        &request.path,
2000                                        &error,
2001                                        &mut context,
2002                                    ),
2003                                    &mut self.error_queue,
2004                                )
2005                            }
2006                        }
2007                    }
2008                }
2009            }
2010        }
2011    }
2012
2013    /// Performs pre update for the engine.
2014    ///
2015    /// Normally, this is called from `Engine::update()`.
2016    /// You should only call this manually if you don't use that method.
2017    ///
2018    /// ## Parameters
2019    ///
2020    /// `lag` - is a reference to time accumulator, that holds remaining amount of time that should be used
2021    /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus stabilizes
2022    /// update rate. The main use of this variable, is to be able to reset `lag` when you doing some heavy
2023    /// calculations in a your game loop (i.e. loading a new level) so the engine won't try to "catch up" with
2024    /// all the time that was spent in heavy calculation. The engine does **not** use this variable itself,
2025    /// but the plugins attach may use it, that's why you need to provide it. If you don't use plugins, then
2026    /// put `&mut 0.0` here.
2027    pub fn pre_update(
2028        &mut self,
2029        dt: f32,
2030        controller: ApplicationLoopController,
2031        lag: &mut f32,
2032        switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
2033    ) {
2034        // Run some plugin and script methods, potentially causing nodes to be added
2035        // or removed. This is where most of the rules of the game happen.
2036        self.update_plugins(dt, controller, lag);
2037        self.handle_scripts(dt);
2038
2039        // Now that the plugins and scripts have made whatever changes are needed, we must respond
2040        // to those changes by updating the scenes and the state of the engine.
2041
2042        self.resource_manager.state().update(dt);
2043        self.handle_model_events();
2044
2045        let window_size = if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
2046            let inner_size = ctx.window.inner_size();
2047            let window_size = Vector2::new(inner_size.width as f32, inner_size.height as f32);
2048            ctx.renderer.update_caches(&self.resource_manager, dt);
2049            window_size
2050        } else {
2051            Vector2::new(1.0, 1.0)
2052        };
2053
2054        for (handle, scene) in self.scenes.pair_iter_mut().filter(|(_, s)| *s.enabled) {
2055            let frame_size =
2056                scene
2057                    .rendering_options
2058                    .render_target
2059                    .as_ref()
2060                    .map_or(window_size, |rt| {
2061                        if let TextureKind::Rectangle { width, height } = rt.data_ref().kind() {
2062                            Vector2::new(width as f32, height as f32)
2063                        } else {
2064                            panic!("only rectangle textures can be used as render target!");
2065                        }
2066                    });
2067
2068            scene.update(
2069                frame_size,
2070                dt,
2071                switches.get(&handle).cloned().unwrap_or_default(),
2072            );
2073        }
2074    }
2075
2076    /// Performs post update for the engine.
2077    ///
2078    /// Normally, this is called from `Engine::update()`.
2079    /// You should only call this manually if you don't use that method.
2080    pub fn post_update(
2081        &mut self,
2082        dt: f32,
2083        ui_update_switches: &UiUpdateSwitches,
2084        lag: &mut f32,
2085        controller: ApplicationLoopController,
2086    ) {
2087        let screen_size = if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
2088            let inner_size = ctx.window.inner_size();
2089            Some(Vector2::new(
2090                inner_size.width as f32,
2091                inner_size.height as f32,
2092            ))
2093        } else {
2094            None
2095        };
2096
2097        let time = instant::Instant::now();
2098        for ui in self.user_interfaces.iter_mut() {
2099            let screen_size = screen_size.unwrap_or_else(|| ui.screen_size());
2100            ui.update(screen_size, dt, ui_update_switches);
2101        }
2102        self.performance_statistics.ui_time = instant::Instant::now() - time;
2103        self.elapsed_time += dt;
2104
2105        if let GraphicsContext::Initialized(_) = self.graphics_context {
2106            self.post_update_plugins(dt, controller, lag);
2107
2108            self.input_state.mouse.speed = Vector2::default();
2109            self.input_state.keyboard.released_keys.clear();
2110            self.input_state.keyboard.pressed_keys.clear();
2111        }
2112    }
2113
2114    /// Returns true if the scene is registered for script processing.
2115    pub fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
2116        self.script_processor.has_scripted_scene(scene)
2117    }
2118
2119    /// Registers a scene for script processing.
2120    pub fn register_scripted_scene(&mut self, scene: Handle<Scene>) {
2121        self.script_processor
2122            .register_scripted_scene(scene, &self.resource_manager)
2123    }
2124
2125    fn handle_scripts(&mut self, dt: f32) {
2126        let time = instant::Instant::now();
2127
2128        self.script_processor.handle_scripts(
2129            &mut self.scenes,
2130            &mut self.plugins,
2131            &self.resource_manager,
2132            &mut self.task_pool,
2133            &mut self.graphics_context,
2134            &mut self.user_interfaces,
2135            dt,
2136            self.elapsed_time,
2137            &self.input_state,
2138            &mut self.error_queue,
2139        );
2140
2141        self.performance_statistics.scripts_time = instant::Instant::now() - time;
2142    }
2143
2144    fn handle_async_tasks(
2145        &mut self,
2146        dt: f32,
2147        controller: ApplicationLoopController,
2148        lag: &mut f32,
2149    ) {
2150        while let Some(result) = self.task_pool.inner().next_task_result() {
2151            if let Some(plugin_task_handler) = self.task_pool.pop_plugin_task_handler(result.id) {
2152                // Handle plugin task.
2153                let mut ctx = PluginContext {
2154                    scenes: &mut self.scenes,
2155                    resource_manager: &self.resource_manager,
2156                    graphics_context: &mut self.graphics_context,
2157                    dt,
2158                    lag,
2159                    user_interfaces: &mut self.user_interfaces,
2160                    serialization_context: &self.serialization_context,
2161                    widget_constructors: &self.widget_constructors,
2162                    dyn_type_constructors: &self.dyn_type_constructors,
2163                    performance_statistics: &self.performance_statistics,
2164                    elapsed_time: self.elapsed_time,
2165                    script_processor: &self.script_processor,
2166                    async_scene_loader: &mut self.async_scene_loader,
2167                    loop_controller: controller,
2168                    task_pool: &mut self.task_pool,
2169                    input_state: &self.input_state,
2170                };
2171                try_enqueue_plugin_error(
2172                    "handle_async_tasks",
2173                    (plugin_task_handler)(result.payload, &mut self.plugins, &mut ctx),
2174                    &mut self.error_queue,
2175                )
2176            } else if let Some(node_task_handler) = self.task_pool.pop_node_task_handler(result.id)
2177            {
2178                // Handle script task.
2179                if let Some(scripted_scene) = self
2180                    .script_processor
2181                    .scripted_scenes
2182                    .iter_mut()
2183                    .find(|e| e.handle == node_task_handler.scene_handle)
2184                {
2185                    let payload = result.payload;
2186                    if let Ok(scene) = self.scenes.try_get_mut(node_task_handler.scene_handle) {
2187                        if let Ok(node) =
2188                            scene.graph.try_get_node_mut(node_task_handler.node_handle)
2189                        {
2190                            if let Some(mut script) = node
2191                                .scripts
2192                                .get_mut(node_task_handler.script_index)
2193                                .and_then(|e| e.script.take())
2194                            {
2195                                let mut ctx = ScriptContext {
2196                                    dt,
2197                                    elapsed_time: self.elapsed_time,
2198                                    plugins: PluginsRefMut(&mut self.plugins),
2199                                    handle: node_task_handler.node_handle,
2200                                    scene,
2201                                    scene_handle: scripted_scene.handle,
2202                                    resource_manager: &self.resource_manager,
2203                                    message_sender: &scripted_scene.message_sender,
2204                                    message_dispatcher: &mut scripted_scene.message_dispatcher,
2205                                    task_pool: &mut self.task_pool,
2206                                    graphics_context: &mut self.graphics_context,
2207                                    user_interfaces: &mut self.user_interfaces,
2208                                    script_index: node_task_handler.script_index,
2209                                    input_state: &self.input_state,
2210                                };
2211                                try_enqueue_plugin_error(
2212                                    "handle_async_tasks",
2213                                    (node_task_handler.closure)(
2214                                        payload,
2215                                        script.deref_mut(),
2216                                        &mut ctx,
2217                                    ),
2218                                    &mut self.error_queue,
2219                                );
2220
2221                                if let Ok(node) =
2222                                    scene.graph.try_get_node_mut(node_task_handler.node_handle)
2223                                {
2224                                    if let Some(entry) =
2225                                        node.scripts.get_mut(node_task_handler.script_index)
2226                                    {
2227                                        if entry.should_be_deleted {
2228                                            Log::verify(scene.graph.script_message_sender.send(
2229                                                NodeScriptMessage::DestroyScript {
2230                                                    script,
2231                                                    handle: node_task_handler.node_handle,
2232                                                    script_index: node_task_handler.script_index,
2233                                                },
2234                                            ));
2235                                        } else {
2236                                            entry.script = Some(script);
2237                                        }
2238                                    }
2239                                }
2240                            }
2241                        }
2242                    }
2243                }
2244            }
2245        }
2246    }
2247
2248    fn update_plugins(&mut self, dt: f32, controller: ApplicationLoopController, lag: &mut f32) {
2249        let time = instant::Instant::now();
2250
2251        if self.plugins_enabled {
2252            // Handle asynchronous tasks first.
2253            self.handle_async_tasks(dt, controller, lag);
2254
2255            // Then update all the plugins.
2256            let mut context = PluginContext {
2257                scenes: &mut self.scenes,
2258                resource_manager: &self.resource_manager,
2259                graphics_context: &mut self.graphics_context,
2260                dt,
2261                lag,
2262                user_interfaces: &mut self.user_interfaces,
2263                serialization_context: &self.serialization_context,
2264                widget_constructors: &self.widget_constructors,
2265                dyn_type_constructors: &self.dyn_type_constructors,
2266                performance_statistics: &self.performance_statistics,
2267                elapsed_time: self.elapsed_time,
2268                script_processor: &self.script_processor,
2269                async_scene_loader: &mut self.async_scene_loader,
2270                loop_controller: controller,
2271                task_pool: &mut self.task_pool,
2272                input_state: &self.input_state,
2273            };
2274
2275            for plugin in self.plugins.iter_mut() {
2276                try_enqueue_plugin_error(
2277                    "update",
2278                    plugin.update(&mut context),
2279                    &mut self.error_queue,
2280                );
2281            }
2282
2283            let mut uis = self
2284                .user_interfaces
2285                .pair_iter()
2286                .map(|(h, _)| h)
2287                .collect::<VecDeque<_>>();
2288
2289            while let Some(ui) = uis.pop_front() {
2290                while let Some(message) = self
2291                    .user_interfaces
2292                    .try_get_mut(ui)
2293                    .ok()
2294                    .and_then(|ui| ui.poll_message())
2295                {
2296                    let mut context = PluginContext {
2297                        scenes: &mut self.scenes,
2298                        resource_manager: &self.resource_manager,
2299                        graphics_context: &mut self.graphics_context,
2300                        dt,
2301                        lag,
2302                        user_interfaces: &mut self.user_interfaces,
2303                        serialization_context: &self.serialization_context,
2304                        widget_constructors: &self.widget_constructors,
2305                        dyn_type_constructors: &self.dyn_type_constructors,
2306                        performance_statistics: &self.performance_statistics,
2307                        elapsed_time: self.elapsed_time,
2308                        script_processor: &self.script_processor,
2309                        async_scene_loader: &mut self.async_scene_loader,
2310                        loop_controller: controller,
2311                        task_pool: &mut self.task_pool,
2312                        input_state: &self.input_state,
2313                    };
2314
2315                    for plugin in self.plugins.iter_mut() {
2316                        try_enqueue_plugin_error(
2317                            "on_ui_message",
2318                            plugin.on_ui_message(&mut context, &message, ui),
2319                            &mut self.error_queue,
2320                        );
2321                    }
2322                }
2323            }
2324        }
2325
2326        self.performance_statistics.plugins_time = instant::Instant::now() - time;
2327    }
2328
2329    fn post_update_plugins(
2330        &mut self,
2331        dt: f32,
2332        controller: ApplicationLoopController,
2333        lag: &mut f32,
2334    ) {
2335        let time = instant::Instant::now();
2336
2337        if self.plugins_enabled {
2338            let mut context = PluginContext {
2339                scenes: &mut self.scenes,
2340                resource_manager: &self.resource_manager,
2341                graphics_context: &mut self.graphics_context,
2342                dt,
2343                lag,
2344                user_interfaces: &mut self.user_interfaces,
2345                serialization_context: &self.serialization_context,
2346                widget_constructors: &self.widget_constructors,
2347                dyn_type_constructors: &self.dyn_type_constructors,
2348                performance_statistics: &self.performance_statistics,
2349                elapsed_time: self.elapsed_time,
2350                script_processor: &self.script_processor,
2351                async_scene_loader: &mut self.async_scene_loader,
2352                loop_controller: controller,
2353                task_pool: &mut self.task_pool,
2354                input_state: &self.input_state,
2355            };
2356
2357            for plugin in self.plugins.iter_mut() {
2358                try_enqueue_plugin_error(
2359                    "post_update",
2360                    plugin.post_update(&mut context),
2361                    &mut self.error_queue,
2362                );
2363            }
2364
2365            while let Some(container) = self.error_queue.pop_back() {
2366                for plugin in self.plugins.iter_mut() {
2367                    if plugin.on_game_error(&mut context, &container.error) {
2368                        continue;
2369                    }
2370                    match container.source {
2371                        GameErrorSource::PluginMethod(method_name) => {
2372                            err!(
2373                                "An error occurred in {method_name} plugin method. Reason: {}",
2374                                container.error
2375                            );
2376                        }
2377                        GameErrorSource::ScriptMethod {
2378                            scene_handle,
2379                            node_handle,
2380                            method_name,
2381                        } => {
2382                            let node_name = context
2383                                .scenes
2384                                .try_get(scene_handle)
2385                                .ok()
2386                                .and_then(|scene| {
2387                                    scene.graph.try_get(node_handle).ok().map(|n| n.name())
2388                                })
2389                                .unwrap_or("<undefined>");
2390
2391                            err!(
2392                                "An error occurred during {method_name} call in {node_handle} \
2393                            node (name: {node_name}). Reason: {}",
2394                                container.error
2395                            );
2396                        }
2397                    }
2398                }
2399            }
2400        }
2401
2402        self.performance_statistics.plugins_time += instant::Instant::now() - time;
2403    }
2404
2405    /// Should be called on every OS event to update the internal state of the engine accordingly.
2406    pub fn handle_os_events(
2407        &mut self,
2408        event: &Event<()>,
2409        dt: f32,
2410        controller: ApplicationLoopController,
2411        lag: &mut f32,
2412    ) {
2413        match event {
2414            Event::WindowEvent { event, .. } => match event {
2415                WindowEvent::KeyboardInput { event, .. } => {
2416                    let keyboard = &mut self.input_state.keyboard;
2417
2418                    match event.state {
2419                        ElementState::Pressed => {
2420                            if keyboard
2421                                .keys
2422                                .get(&event.physical_key)
2423                                .is_none_or(|state| *state == ElementState::Released)
2424                            {
2425                                keyboard.pressed_keys.insert(event.physical_key);
2426                            }
2427                        }
2428                        ElementState::Released => {
2429                            if keyboard
2430                                .keys
2431                                .get(&event.physical_key)
2432                                .is_some_and(|state| *state == ElementState::Pressed)
2433                            {
2434                                keyboard.released_keys.insert(event.physical_key);
2435                            }
2436                        }
2437                    }
2438
2439                    keyboard.keys.insert(event.physical_key, event.state);
2440                }
2441                WindowEvent::CursorMoved { position, .. } => {
2442                    self.input_state.mouse.position =
2443                        Vector2::new(position.x as f32, position.y as f32);
2444                }
2445                _ => (),
2446            },
2447            Event::DeviceEvent { event, .. } => match event {
2448                DeviceEvent::MouseMotion { delta } => {
2449                    self.input_state.mouse.speed = Vector2::new(delta.0 as f32, delta.1 as f32);
2450                }
2451                DeviceEvent::Button { button, state } => {
2452                    let mouse = &mut self.input_state.mouse;
2453
2454                    match *state {
2455                        ElementState::Pressed => {
2456                            if mouse
2457                                .buttons_state
2458                                .get(button)
2459                                .is_none_or(|state| *state == ElementState::Released)
2460                            {
2461                                mouse.pressed_buttons.insert(*button);
2462                            }
2463                        }
2464                        ElementState::Released => {
2465                            if mouse
2466                                .buttons_state
2467                                .get(button)
2468                                .is_some_and(|state| *state == ElementState::Pressed)
2469                            {
2470                                mouse.released_buttons.insert(*button);
2471                            }
2472                        }
2473                    }
2474
2475                    mouse.buttons_state.insert(*button, *state);
2476                }
2477                _ => (),
2478            },
2479            _ => (),
2480        }
2481
2482        if self.plugins_enabled {
2483            for plugin in self.plugins.iter_mut() {
2484                let ctx = PluginContext {
2485                    scenes: &mut self.scenes,
2486                    resource_manager: &self.resource_manager,
2487                    graphics_context: &mut self.graphics_context,
2488                    dt,
2489                    lag,
2490                    user_interfaces: &mut self.user_interfaces,
2491                    serialization_context: &self.serialization_context,
2492                    widget_constructors: &self.widget_constructors,
2493                    dyn_type_constructors: &self.dyn_type_constructors,
2494                    performance_statistics: &self.performance_statistics,
2495                    elapsed_time: self.elapsed_time,
2496                    script_processor: &self.script_processor,
2497                    async_scene_loader: &mut self.async_scene_loader,
2498                    loop_controller: controller,
2499                    task_pool: &mut self.task_pool,
2500                    input_state: &self.input_state,
2501                };
2502
2503                try_enqueue_plugin_error(
2504                    "on_os_event",
2505                    plugin.on_os_event(event, ctx),
2506                    &mut self.error_queue,
2507                );
2508            }
2509        }
2510    }
2511
2512    pub(crate) fn handle_graphics_context_created_by_plugins(
2513        &mut self,
2514        dt: f32,
2515        controller: ApplicationLoopController,
2516        lag: &mut f32,
2517    ) {
2518        if self.plugins_enabled {
2519            for plugin in self.plugins.iter_mut() {
2520                let ctx = PluginContext {
2521                    scenes: &mut self.scenes,
2522                    resource_manager: &self.resource_manager,
2523                    graphics_context: &mut self.graphics_context,
2524                    dt,
2525                    lag,
2526                    user_interfaces: &mut self.user_interfaces,
2527                    serialization_context: &self.serialization_context,
2528                    widget_constructors: &self.widget_constructors,
2529                    dyn_type_constructors: &self.dyn_type_constructors,
2530                    performance_statistics: &self.performance_statistics,
2531                    elapsed_time: self.elapsed_time,
2532                    script_processor: &self.script_processor,
2533                    async_scene_loader: &mut self.async_scene_loader,
2534                    loop_controller: controller,
2535                    task_pool: &mut self.task_pool,
2536                    input_state: &self.input_state,
2537                };
2538
2539                try_enqueue_plugin_error(
2540                    "on_graphics_context_initialized",
2541                    plugin.on_graphics_context_initialized(ctx),
2542                    &mut self.error_queue,
2543                );
2544            }
2545        }
2546    }
2547
2548    pub(crate) fn handle_graphics_context_destroyed_by_plugins(
2549        &mut self,
2550        dt: f32,
2551        controller: ApplicationLoopController,
2552        lag: &mut f32,
2553    ) {
2554        if self.plugins_enabled {
2555            for plugin in self.plugins.iter_mut() {
2556                let ctx = PluginContext {
2557                    scenes: &mut self.scenes,
2558                    resource_manager: &self.resource_manager,
2559                    graphics_context: &mut self.graphics_context,
2560                    dt,
2561                    lag,
2562                    user_interfaces: &mut self.user_interfaces,
2563                    serialization_context: &self.serialization_context,
2564                    widget_constructors: &self.widget_constructors,
2565                    dyn_type_constructors: &self.dyn_type_constructors,
2566                    performance_statistics: &self.performance_statistics,
2567                    elapsed_time: self.elapsed_time,
2568                    script_processor: &self.script_processor,
2569                    async_scene_loader: &mut self.async_scene_loader,
2570                    loop_controller: controller,
2571                    task_pool: &mut self.task_pool,
2572                    input_state: &self.input_state,
2573                };
2574
2575                try_enqueue_plugin_error(
2576                    "on_graphics_context_destroyed",
2577                    plugin.on_graphics_context_destroyed(ctx),
2578                    &mut self.error_queue,
2579                );
2580            }
2581        }
2582    }
2583
2584    pub(crate) fn handle_before_rendering_by_plugins(
2585        &mut self,
2586        dt: f32,
2587        controller: ApplicationLoopController,
2588        lag: &mut f32,
2589    ) {
2590        if self.plugins_enabled {
2591            for plugin in self.plugins.iter_mut() {
2592                let ctx = PluginContext {
2593                    scenes: &mut self.scenes,
2594                    resource_manager: &self.resource_manager,
2595                    graphics_context: &mut self.graphics_context,
2596                    dt,
2597                    lag,
2598                    user_interfaces: &mut self.user_interfaces,
2599                    serialization_context: &self.serialization_context,
2600                    widget_constructors: &self.widget_constructors,
2601                    dyn_type_constructors: &self.dyn_type_constructors,
2602                    performance_statistics: &self.performance_statistics,
2603                    elapsed_time: self.elapsed_time,
2604                    script_processor: &self.script_processor,
2605                    async_scene_loader: &mut self.async_scene_loader,
2606                    loop_controller: controller,
2607                    task_pool: &mut self.task_pool,
2608                    input_state: &self.input_state,
2609                };
2610
2611                try_enqueue_plugin_error(
2612                    "before_rendering",
2613                    plugin.before_rendering(ctx),
2614                    &mut self.error_queue,
2615                );
2616            }
2617        }
2618    }
2619
2620    /// Passes specified OS event to every script of the specified scene.
2621    ///
2622    /// # Important notes
2623    ///
2624    /// This method is intended to be used by the editor and game runner. If you're using the
2625    /// engine as a framework, then you should not call this method because you'll most likely
2626    /// do something wrong.
2627    pub(crate) fn handle_os_event_by_scripts(
2628        &mut self,
2629        event: &Event<()>,
2630        scene_handle: Handle<Scene>,
2631        dt: f32,
2632    ) {
2633        if let Some(scripted_scene) = self
2634            .script_processor
2635            .scripted_scenes
2636            .iter_mut()
2637            .find(|s| s.handle == scene_handle)
2638        {
2639            let scene = &mut self.scenes[scene_handle];
2640            if *scene.enabled {
2641                process_scripts(
2642                    "on_os_event",
2643                    scene,
2644                    scene_handle,
2645                    &mut self.plugins,
2646                    &self.resource_manager,
2647                    &scripted_scene.message_sender,
2648                    &mut scripted_scene.message_dispatcher,
2649                    &mut self.task_pool,
2650                    &mut self.graphics_context,
2651                    &mut self.user_interfaces,
2652                    dt,
2653                    self.elapsed_time,
2654                    &self.input_state,
2655                    &mut self.error_queue,
2656                    |script, context| {
2657                        if script.initialized && script.started {
2658                            script.on_os_event(event, context)
2659                        } else {
2660                            Ok(())
2661                        }
2662                    },
2663                )
2664            }
2665        }
2666    }
2667
2668    /// Handle hot-reloading of resources.
2669    ///
2670    /// Normally, this is called from `Engine::update()`.
2671    /// You should only call this manually if you don't use that method.
2672    pub fn handle_model_events(&mut self) {
2673        while let Ok(event) = self.model_events_receiver.try_recv() {
2674            if let ResourceEvent::Reloaded(resource) = event {
2675                if let Some(model) = resource.try_cast::<Model>() {
2676                    Log::info(format!(
2677                        "A model resource {} was reloaded, propagating changes...",
2678                        model.resource_uuid()
2679                    ));
2680
2681                    // Build resource dependency graph and resolve it first.
2682                    ResourceDependencyGraph::new(model, self.resource_manager.clone()).resolve();
2683
2684                    Log::info("Propagating changes to active scenes...");
2685
2686                    // Resolve all scenes.
2687                    // TODO: This might be inefficient if there is bunch of scenes loaded,
2688                    // however this seems to be very rare case so it should be ok.
2689                    for scene in self.scenes.iter_mut() {
2690                        scene.resolve();
2691                    }
2692                }
2693            }
2694        }
2695    }
2696
2697    /// Performs rendering of single frame, must be called from your game loop, otherwise you won't
2698    /// see anything.
2699    #[inline]
2700    pub fn render(&mut self) -> Result<(), FrameworkError> {
2701        for ui in self.user_interfaces.iter_mut() {
2702            ui.set_time(self.elapsed_time);
2703        }
2704
2705        if let GraphicsContext::Initialized(ref mut ctx) = self.graphics_context {
2706            ctx.renderer.render_and_swap_buffers(
2707                &self.scenes,
2708                self.elapsed_time,
2709                self.user_interfaces
2710                    .iter_mut()
2711                    .filter(|ui| match ui.render_mode {
2712                        RenderMode::EveryFrame => true,
2713                        RenderMode::OnChanges => ui.need_render,
2714                    })
2715                    .map(|ui| {
2716                        ui.need_render = false;
2717                        UiRenderInfo {
2718                            ui,
2719                            render_target: ui.render_target.clone(),
2720                            clear_color: Default::default(),
2721                            resource_manager: &self.resource_manager,
2722                        }
2723                    }),
2724                &ctx.window,
2725                &self.resource_manager,
2726            )?;
2727        }
2728
2729        Ok(())
2730    }
2731
2732    /// Enables or disables registered plugins.
2733    pub(crate) fn enable_plugins(
2734        &mut self,
2735        scene_path: Option<&str>,
2736        enabled: bool,
2737        controller: ApplicationLoopController,
2738    ) {
2739        if self.plugins_enabled != enabled {
2740            self.plugins_enabled = enabled;
2741
2742            if self.plugins_enabled {
2743                // Create and initialize instances.
2744                for plugin in self.plugins.iter_mut() {
2745                    let ctx = PluginContext {
2746                        scenes: &mut self.scenes,
2747                        resource_manager: &self.resource_manager,
2748                        graphics_context: &mut self.graphics_context,
2749                        dt: 0.0,
2750                        lag: &mut 0.0,
2751                        user_interfaces: &mut self.user_interfaces,
2752                        serialization_context: &self.serialization_context,
2753                        widget_constructors: &self.widget_constructors,
2754                        dyn_type_constructors: &self.dyn_type_constructors,
2755                        performance_statistics: &self.performance_statistics,
2756                        elapsed_time: self.elapsed_time,
2757                        script_processor: &self.script_processor,
2758                        async_scene_loader: &mut self.async_scene_loader,
2759                        loop_controller: controller,
2760                        task_pool: &mut self.task_pool,
2761                        input_state: &self.input_state,
2762                    };
2763
2764                    try_enqueue_plugin_error(
2765                        "init",
2766                        plugin.init(scene_path, ctx),
2767                        &mut self.error_queue,
2768                    );
2769                }
2770            } else {
2771                self.handle_scripts(0.0);
2772
2773                for mut plugin in self.plugins.drain(..) {
2774                    let ctx = PluginContext {
2775                        scenes: &mut self.scenes,
2776                        resource_manager: &self.resource_manager,
2777                        graphics_context: &mut self.graphics_context,
2778                        dt: 0.0,
2779                        lag: &mut 0.0,
2780                        user_interfaces: &mut self.user_interfaces,
2781                        serialization_context: &self.serialization_context,
2782                        widget_constructors: &self.widget_constructors,
2783                        dyn_type_constructors: &self.dyn_type_constructors,
2784                        performance_statistics: &self.performance_statistics,
2785                        elapsed_time: self.elapsed_time,
2786                        script_processor: &self.script_processor,
2787                        async_scene_loader: &mut self.async_scene_loader,
2788                        loop_controller: controller,
2789                        task_pool: &mut self.task_pool,
2790                        input_state: &self.input_state,
2791                    };
2792
2793                    // Deinit plugin first.
2794                    try_enqueue_plugin_error(
2795                        "on_deinit",
2796                        plugin.on_deinit(ctx),
2797                        &mut self.error_queue,
2798                    );
2799                }
2800            }
2801        }
2802    }
2803
2804    fn register_plugin_internal(
2805        serialization_context: &Arc<SerializationContext>,
2806        widget_constructors: &Arc<WidgetConstructorContainer>,
2807        dyn_type_constructors: &Arc<DynTypeConstructorContainer>,
2808        resource_manager: &ResourceManager,
2809        plugin: &dyn Plugin,
2810        error_queue: &mut ErrorQueue,
2811    ) {
2812        try_enqueue_plugin_error(
2813            "register",
2814            plugin.register(PluginRegistrationContext {
2815                serialization_context,
2816                widget_constructors,
2817                dyn_type_constructors,
2818                resource_manager,
2819            }),
2820            error_queue,
2821        );
2822    }
2823
2824    fn register_plugin(&mut self, plugin: &dyn Plugin) {
2825        Self::register_plugin_internal(
2826            &self.serialization_context,
2827            &self.widget_constructors,
2828            &self.dyn_type_constructors,
2829            &self.resource_manager,
2830            plugin,
2831            &mut self.error_queue,
2832        )
2833    }
2834
2835    /// Adds a new static plugin.
2836    pub fn add_plugin<P>(&mut self, plugin: P)
2837    where
2838        P: Plugin + 'static,
2839    {
2840        self.register_plugin(&plugin);
2841
2842        self.plugins.push(PluginContainer::Static(Box::new(plugin)));
2843    }
2844
2845    /// Tries to add a new dynamic plugin. This method attempts to load a dynamic library by the
2846    /// given path and searches for `fyrox_plugin` function. This function is called to create a
2847    /// plugin instance. This method will fail if there's no dynamic library at the given path or
2848    /// the `fyrox_plugin` function is not found.
2849    ///
2850    /// # Hot reloading
2851    ///
2852    /// This method can enable hot reloading for the plugin, by setting `reload_when_changed` parameter
2853    /// to `true`. When enabled, the engine will clone the library to implementation-defined path
2854    /// and load it. It will setup file system watcher to receive changes from the OS and reload
2855    /// the plugin.
2856    pub fn add_dynamic_plugin<P>(
2857        &mut self,
2858        path: P,
2859        reload_when_changed: bool,
2860        use_relative_paths: bool,
2861    ) -> Result<&dyn Plugin, String>
2862    where
2863        P: AsRef<Path> + 'static,
2864    {
2865        Ok(self.add_dynamic_plugin_custom(DyLibDynamicPlugin::new(
2866            path,
2867            reload_when_changed,
2868            use_relative_paths,
2869        )?))
2870    }
2871
2872    /// Adds a new abstract dynamic plugin
2873    pub fn add_dynamic_plugin_custom<P>(&mut self, plugin: P) -> &dyn Plugin
2874    where
2875        P: DynamicPlugin + 'static,
2876    {
2877        let display_name = plugin.display_name();
2878
2879        let plugin_container = PluginContainer::Dynamic(Box::new(plugin));
2880
2881        self.register_plugin(plugin_container.deref());
2882        self.plugins.push(plugin_container);
2883
2884        Log::info(format!("Plugin {display_name:?} was loaded successfully"));
2885
2886        &**self.plugins.last().unwrap()
2887    }
2888
2889    /// Tries to reload a specified plugin. This method tries to perform least invasive reloading, by
2890    /// only detaching parts from the scenes and engine internals, that belongs to reloadable plugin.
2891    pub fn reload_plugin(
2892        &mut self,
2893        plugin_index: usize,
2894        dt: f32,
2895        controller: ApplicationLoopController,
2896        lag: &mut f32,
2897    ) -> Result<(), String> {
2898        let plugin_container = &mut self.plugins[plugin_index];
2899        let PluginContainer::Dynamic(plugin) = plugin_container else {
2900            return Err(format!(
2901                "Plugin {plugin_index} is static and cannot be reloaded!",
2902            ));
2903        };
2904
2905        if !plugin.is_loaded() {
2906            // TODO: this means that something bad happened during plugin reloading.
2907            // don't we want to recover from this situation by trying to load it again
2908            // (maybe with clearing  `need_reload` flag, to perform new attempt only when something is changed)
2909            return Err(format!("Cannot reload unloaded plugin {plugin_index}!"));
2910        }
2911        plugin.prepare_to_reload();
2912
2913        let plugin_type_id = plugin.as_loaded_ref().type_id();
2914        let plugin_assembly_name = plugin.as_loaded_ref().assembly_name();
2915
2916        // Collect all the data that belongs to the plugin
2917        let mut scenes_state = Vec::new();
2918        for (scene_handle, scene) in self.scenes.pair_iter_mut() {
2919            if let Some(data) = hotreload::SceneState::try_create_from_plugin(
2920                scene_handle,
2921                scene,
2922                &self.serialization_context,
2923                plugin.as_loaded_ref(),
2924            )? {
2925                scenes_state.push(data);
2926            }
2927        }
2928
2929        // Check every prefab for plugin content.
2930        let mut prefab_scenes = Vec::new();
2931        let rm_state = self.resource_manager.state();
2932        for resource in rm_state.resources().iter() {
2933            if let Some(model) = resource.try_cast::<Model>() {
2934                let mut model_state = model.state();
2935                if let Some(data) = model_state.data() {
2936                    if let Some(scene_state) = hotreload::SceneState::try_create_from_plugin(
2937                        Handle::NONE,
2938                        &mut data.scene,
2939                        &self.serialization_context,
2940                        plugin.as_loaded_ref(),
2941                    )? {
2942                        prefab_scenes.push((model.clone(), scene_state));
2943                    }
2944                }
2945            }
2946        }
2947        drop(rm_state);
2948
2949        // Search for script constructors, that belongs to dynamic plugins and remove them.
2950        let mut constructors = FxHashSet::default();
2951        for (type_uuid, constructor) in self.serialization_context.script_constructors.map().iter()
2952        {
2953            if constructor.assembly_name == plugin_assembly_name {
2954                constructors.insert(*type_uuid);
2955            }
2956        }
2957        for type_uuid in constructors.iter() {
2958            self.serialization_context
2959                .script_constructors
2960                .remove(*type_uuid);
2961        }
2962
2963        // Search for node constructors, that belongs to dynamic plugins and remove them.
2964        let mut constructors = FxHashSet::default();
2965        for (type_uuid, constructor) in self.serialization_context.node_constructors.map().iter() {
2966            if constructor.assembly_name == plugin_assembly_name {
2967                constructors.insert(*type_uuid);
2968            }
2969        }
2970        for type_uuid in constructors.iter() {
2971            self.serialization_context
2972                .node_constructors
2973                .remove(*type_uuid);
2974        }
2975
2976        // Search for widget constructors, that belongs to dynamic plugins and remove them.
2977        let mut constructors = FxHashSet::default();
2978        for (type_uuid, constructor) in self.widget_constructors.map().iter() {
2979            if constructor.assembly_name == plugin_assembly_name {
2980                constructors.insert(*type_uuid);
2981            }
2982        }
2983        for type_uuid in constructors.iter() {
2984            self.widget_constructors.remove(*type_uuid);
2985        }
2986
2987        // Reload resources, that belongs to the plugin.
2988        {
2989            let mut resources_to_reload = FxHashSet::default();
2990            let mut state = self.resource_manager.state();
2991            for resource in state.resources().iter() {
2992                let data = resource.lock();
2993                if let ResourceState::Ok { ref data, .. } = data.state {
2994                    data.as_reflect(&mut |reflect| {
2995                        if reflect.assembly_name() == plugin_assembly_name {
2996                            resources_to_reload.insert(resource.clone());
2997                        }
2998                    })
2999                }
3000            }
3001
3002            for resource_to_reload in resources_to_reload.iter() {
3003                Log::info(format!(
3004                    "Reloading {:?} resource, because it is used in plugin {plugin_assembly_name}",
3005                    state.resource_path(resource_to_reload)
3006                ));
3007
3008                state.reload_resource(resource_to_reload.clone());
3009            }
3010
3011            drop(state);
3012
3013            block_on(join_all(resources_to_reload));
3014        }
3015
3016        // Unload custom render passes (if any).
3017        if let GraphicsContext::Initialized(ref mut graphics_context) = self.graphics_context {
3018            let render_passes = graphics_context.renderer.render_passes().to_vec();
3019            for render_pass in render_passes {
3020                if render_pass.borrow().source_type_id() == plugin_type_id {
3021                    graphics_context.renderer.remove_render_pass(render_pass);
3022                }
3023            }
3024        }
3025
3026        let mut visitor = hotreload::make_writing_visitor();
3027        plugin
3028            .as_loaded_mut()
3029            .visit("Plugin", &mut visitor)
3030            .map_err(|e| e.to_string())?;
3031        let mut binary_blob = Cursor::new(Vec::<u8>::new());
3032        visitor
3033            .save_binary_to_memory(&mut binary_blob)
3034            .map_err(|e| e.to_string())?;
3035
3036        Log::info(format!(
3037            "Plugin {plugin_index} was serialized successfully!"
3038        ));
3039
3040        // Explicitly drop the visitor to prevent any destructors from the previous version of
3041        // the plugin to run at the end of the scope. This could happen, because the visitor
3042        // manages serialized smart pointers and if they'll be kept alive longer than the plugin
3043        // there's a very high chance of hard crash.
3044        drop(visitor);
3045
3046        let binary_blob = binary_blob.into_inner();
3047
3048        plugin.reload(&mut |plugin| {
3049            // Re-register the plugin. This is needed, because it might contain new script/node/widget
3050            // types (or removed ones too). This is done right before deserialization, because plugin
3051            // might contain some entities, that have dynamic registration.
3052            Self::register_plugin_internal(
3053                &self.serialization_context,
3054                &self.widget_constructors,
3055                &self.dyn_type_constructors,
3056                &self.resource_manager,
3057                plugin,
3058                &mut self.error_queue,
3059            );
3060
3061            // New plugins may add custom resources and we must re-scan the data folder to include
3062            // such resources in the registry.
3063            self.resource_manager.update_or_load_registry();
3064
3065            let mut visitor = hotreload::make_reading_visitor(
3066                &binary_blob,
3067                &self.serialization_context,
3068                &self.resource_manager,
3069                &self.widget_constructors,
3070                &self.dyn_type_constructors,
3071            )
3072            .map_err(|e| e.to_string())?;
3073
3074            plugin
3075                .visit("Plugin", &mut visitor)
3076                .map_err(|e| e.to_string())?;
3077            Ok(())
3078        })?;
3079
3080        // Deserialize prefab scene content.
3081        for (model, scene_state) in prefab_scenes {
3082            Log::info(format!(
3083                "Deserializing {} prefab content...",
3084                model.resource_uuid()
3085            ));
3086
3087            scene_state.deserialize_into_prefab_scene(
3088                &model,
3089                &self.serialization_context,
3090                &self.resource_manager,
3091                &self.widget_constructors,
3092                &self.dyn_type_constructors,
3093            )?;
3094        }
3095
3096        // Deserialize scene content.
3097        for scene_state in scenes_state {
3098            let scene = &mut self.scenes[scene_state.scene];
3099            scene_state.deserialize_into_scene(
3100                scene,
3101                &self.serialization_context,
3102                &self.resource_manager,
3103                &self.widget_constructors,
3104                &self.dyn_type_constructors,
3105            )?;
3106        }
3107
3108        // Call `on_loaded` for plugins, so they could restore some runtime non-serializable state.
3109        let ctx = PluginContext {
3110            scenes: &mut self.scenes,
3111            resource_manager: &self.resource_manager,
3112            user_interfaces: &mut self.user_interfaces,
3113            graphics_context: &mut self.graphics_context,
3114            dt,
3115            lag,
3116            serialization_context: &self.serialization_context,
3117            widget_constructors: &self.widget_constructors,
3118            dyn_type_constructors: &self.dyn_type_constructors,
3119            performance_statistics: &Default::default(),
3120            elapsed_time: self.elapsed_time,
3121            script_processor: &self.script_processor,
3122            async_scene_loader: &mut self.async_scene_loader,
3123            loop_controller: controller,
3124            task_pool: &mut self.task_pool,
3125            input_state: &self.input_state,
3126        };
3127        try_enqueue_plugin_error(
3128            "on_loaded",
3129            plugin.as_loaded_mut().on_loaded(ctx),
3130            &mut self.error_queue,
3131        );
3132
3133        Log::info(format!("Plugin {plugin_index} was successfully reloaded!"));
3134
3135        Ok(())
3136    }
3137
3138    /// Returns a reference to the plugins.
3139    pub fn plugins(&self) -> &[PluginContainer] {
3140        &self.plugins
3141    }
3142
3143    /// Returns a mutable reference to the plugins.
3144    pub fn plugins_mut(&mut self) -> &mut [PluginContainer] {
3145        &mut self.plugins
3146    }
3147
3148    /// Tries to reload all dynamic plugins registered in the engine, that needs to be reloaded.
3149    pub fn reload_dynamic_plugins<F>(
3150        &mut self,
3151        dt: f32,
3152        controller: ApplicationLoopController,
3153        lag: &mut f32,
3154        mut on_reloaded: F,
3155    ) -> Result<(), String>
3156    where
3157        F: FnMut(&dyn Plugin),
3158    {
3159        for plugin_index in 0..self.plugins.len() {
3160            if let PluginContainer::Dynamic(plugin) = &self.plugins[plugin_index] {
3161                if plugin.is_reload_needed_now() {
3162                    self.reload_plugin(plugin_index, dt, controller, lag)?;
3163
3164                    on_reloaded(self.plugins[plugin_index].deref_mut());
3165                }
3166            }
3167        }
3168
3169        Ok(())
3170    }
3171}
3172
3173impl Drop for Engine {
3174    fn drop(&mut self) {
3175        // Destroy all scenes first and correctly destroy all script instances.
3176        // This will ensure that any `on_destroy` logic will be executed before
3177        // engine destroyed.
3178        let scenes = self
3179            .scenes
3180            .pair_iter()
3181            .map(|(h, _)| h)
3182            .collect::<Vec<Handle<Scene>>>();
3183
3184        for handle in scenes {
3185            self.scenes.remove(handle);
3186        }
3187
3188        // Finally disable plugins.
3189        self.enable_plugins(
3190            None,
3191            false,
3192            ApplicationLoopController::Headless {
3193                running: &Default::default(),
3194            },
3195        );
3196    }
3197}
3198
3199#[cfg(test)]
3200mod test {
3201    use crate::engine::ApplicationLoopController;
3202    use crate::plugin::error::GameResult;
3203    use crate::scene::pivot::Pivot;
3204    use crate::{
3205        asset::manager::ResourceManager,
3206        core::{
3207            pool::Handle, reflect::prelude::*, task::TaskPool, type_traits::prelude::*,
3208            visitor::prelude::*,
3209        },
3210        engine::{task::TaskPoolHandler, GraphicsContext, ScriptProcessor},
3211        graph::SceneGraph,
3212        scene::{base::BaseBuilder, node::Node, pivot::PivotBuilder, Scene, SceneContainer},
3213        script::{
3214            ScriptContext, ScriptDeinitContext, ScriptMessageContext, ScriptMessagePayload,
3215            ScriptTrait,
3216        },
3217    };
3218    use fyrox_resource::io::FsResourceIo;
3219    use fyrox_ui::UiContainer;
3220    use std::cell::Cell;
3221    use std::sync::{
3222        mpsc::{self, Sender, TryRecvError},
3223        Arc,
3224    };
3225
3226    #[derive(PartialEq, Eq, Copy, Clone, Debug)]
3227    struct Source {
3228        node_handle: Handle<Node>,
3229        script_index: usize,
3230    }
3231
3232    impl Source {
3233        fn from_ctx(ctx: &ScriptContext) -> Self {
3234            Self {
3235                node_handle: ctx.handle,
3236                script_index: ctx.script_index,
3237            }
3238        }
3239
3240        fn from_deinit_ctx(ctx: &ScriptDeinitContext) -> Self {
3241            Self {
3242                node_handle: ctx.node_handle,
3243                script_index: ctx.script_index,
3244            }
3245        }
3246
3247        fn from_msg_ctx(ctx: &ScriptMessageContext) -> Self {
3248            Self {
3249                node_handle: ctx.handle,
3250                script_index: ctx.script_index,
3251            }
3252        }
3253    }
3254
3255    #[allow(clippy::enum_variant_names)]
3256    #[derive(PartialEq, Eq, Clone, Debug)]
3257    enum Event {
3258        Initialized(Source),
3259        Started(Source),
3260        Updated(Source),
3261        Destroyed(Source),
3262        EventReceived(Source),
3263    }
3264
3265    #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3266    #[type_uuid(id = "2569de84-d4b2-427d-969b-d5c7b31a0ba6")]
3267    struct MyScript {
3268        #[reflect(hidden)]
3269        #[visit(skip)]
3270        sender: Sender<Event>,
3271        spawned: bool,
3272    }
3273
3274    impl ScriptTrait for MyScript {
3275        fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3276            self.sender
3277                .send(Event::Initialized(Source::from_ctx(ctx)))
3278                .unwrap();
3279
3280            // Spawn new entity with script.
3281            let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3282                sender: self.sender.clone(),
3283            }))
3284            .build(&mut ctx.scene.graph);
3285            assert_eq!(handle, Handle::<Pivot>::new(2, 1));
3286
3287            Ok(())
3288        }
3289
3290        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3291            self.sender
3292                .send(Event::Started(Source::from_ctx(ctx)))
3293                .unwrap();
3294
3295            // Spawn new entity with script.
3296            let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3297                sender: self.sender.clone(),
3298            }))
3299            .build(&mut ctx.scene.graph);
3300            assert_eq!(handle, Handle::<Pivot>::new(3, 1));
3301
3302            Ok(())
3303        }
3304
3305        fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3306            self.sender
3307                .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3308                .unwrap();
3309
3310            Ok(())
3311        }
3312
3313        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3314            self.sender
3315                .send(Event::Updated(Source::from_ctx(ctx)))
3316                .unwrap();
3317
3318            if !self.spawned {
3319                // Spawn new entity with script.
3320                PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3321                    sender: self.sender.clone(),
3322                }))
3323                .build(&mut ctx.scene.graph);
3324
3325                self.spawned = true;
3326            }
3327
3328            Ok(())
3329        }
3330    }
3331
3332    #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3333    #[type_uuid(id = "1cebacd9-b500-4753-93be-39db344add21")]
3334    struct MySubScript {
3335        #[reflect(hidden)]
3336        #[visit(skip)]
3337        sender: Sender<Event>,
3338    }
3339
3340    impl ScriptTrait for MySubScript {
3341        fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3342            self.sender
3343                .send(Event::Initialized(Source::from_ctx(ctx)))
3344                .unwrap();
3345            Ok(())
3346        }
3347
3348        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3349            self.sender
3350                .send(Event::Started(Source::from_ctx(ctx)))
3351                .unwrap();
3352            Ok(())
3353        }
3354
3355        fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3356            self.sender
3357                .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3358                .unwrap();
3359            Ok(())
3360        }
3361
3362        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3363            self.sender
3364                .send(Event::Updated(Source::from_ctx(ctx)))
3365                .unwrap();
3366            Ok(())
3367        }
3368    }
3369
3370    #[test]
3371    fn test_order() {
3372        let resource_manager =
3373            ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3374        let mut scene = Scene::new();
3375
3376        let (tx, rx) = mpsc::channel();
3377
3378        let node_handle = PivotBuilder::new(
3379            BaseBuilder::new()
3380                .with_script(MyScript {
3381                    sender: tx.clone(),
3382                    spawned: false,
3383                })
3384                .with_script(MySubScript { sender: tx }),
3385        )
3386        .build(&mut scene.graph)
3387        .to_base();
3388        assert_eq!(node_handle, Handle::<Pivot>::new(1, 1));
3389
3390        let node_handle_0 = Source {
3391            node_handle,
3392            script_index: 0,
3393        };
3394        let node_handle_1 = Source {
3395            node_handle,
3396            script_index: 1,
3397        };
3398
3399        let mut scene_container = SceneContainer::new(Default::default());
3400
3401        let scene_handle = scene_container.add(scene);
3402
3403        let mut script_processor = ScriptProcessor::default();
3404
3405        script_processor.register_scripted_scene(scene_handle, &resource_manager);
3406
3407        let handle_on_init = Source {
3408            node_handle: Handle::new(2, 1),
3409            script_index: 0,
3410        };
3411        let handle_on_start = Source {
3412            node_handle: Handle::new(3, 1),
3413            script_index: 0,
3414        };
3415        let handle_on_update1 = Source {
3416            node_handle: Handle::new(4, 1),
3417            script_index: 0,
3418        };
3419        let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3420        let mut gc = GraphicsContext::Uninitialized(Default::default());
3421        let mut user_interfaces = UiContainer::default();
3422
3423        for iteration in 0..3 {
3424            script_processor.handle_scripts(
3425                &mut scene_container,
3426                &mut Vec::new(),
3427                &resource_manager,
3428                &mut task_pool,
3429                &mut gc,
3430                &mut user_interfaces,
3431                0.0,
3432                0.0,
3433                &Default::default(),
3434                &mut Default::default(),
3435            );
3436
3437            match iteration {
3438                0 => {
3439                    assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_0)));
3440                    assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_1)));
3441                    assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_init)));
3442                    assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_0)));
3443                    assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_1)));
3444                    assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_init)));
3445                    assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_start)));
3446                    assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_start)));
3447                    assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3448                    assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3449                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3450                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3451                    assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_update1)));
3452                    assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_update1)));
3453                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3454                }
3455                1 => {
3456                    assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3457                    assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3458                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3459                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3460                    assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3461                    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3462
3463                    // Now destroy every node with script, next iteration should correctly destroy attached scripts.
3464                    let graph = &mut scene_container[scene_handle].graph;
3465                    graph.remove_node(node_handle);
3466                    graph.remove_node(handle_on_init.node_handle);
3467                    graph.remove_node(handle_on_start.node_handle);
3468                    graph.remove_node(handle_on_update1.node_handle);
3469                }
3470                2 => {
3471                    assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_0)));
3472                    assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_1)));
3473                    assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_init)));
3474                    assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_start)));
3475                    assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_update1)));
3476
3477                    // Every instance holding sender died, so receiver is disconnected from sender.
3478                    assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
3479                }
3480                _ => (),
3481            }
3482        }
3483    }
3484
3485    #[derive(Debug, ScriptMessagePayload)]
3486    enum MyMessage {
3487        Foo(usize),
3488        Bar(String),
3489    }
3490
3491    #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3492    #[type_uuid(id = "bf2976ad-f41d-4de6-9a32-b1a293956058")]
3493    struct ScriptListeningToMessages {
3494        index: u32,
3495        #[reflect(hidden)]
3496        #[visit(skip)]
3497        sender: Sender<Event>,
3498    }
3499
3500    impl ScriptTrait for ScriptListeningToMessages {
3501        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3502            ctx.message_dispatcher.subscribe_to::<MyMessage>(ctx.handle);
3503            Ok(())
3504        }
3505
3506        fn on_message(
3507            &mut self,
3508            message: &mut dyn ScriptMessagePayload,
3509            ctx: &mut ScriptMessageContext,
3510        ) -> GameResult {
3511            let typed_message = message.downcast_ref::<MyMessage>().unwrap();
3512            match self.index {
3513                0 => {
3514                    if let MyMessage::Foo(num) = typed_message {
3515                        assert_eq!(*num, 123);
3516                        self.sender
3517                            .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3518                            .unwrap();
3519                    } else {
3520                        unreachable!()
3521                    }
3522                }
3523                1 => {
3524                    if let MyMessage::Bar(string) = typed_message {
3525                        assert_eq!(string, "Foobar");
3526                        self.sender
3527                            .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3528                            .unwrap();
3529                    } else {
3530                        unreachable!()
3531                    }
3532                }
3533                _ => (),
3534            }
3535
3536            self.index += 1;
3537
3538            Ok(())
3539        }
3540    }
3541
3542    #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3543    #[type_uuid(id = "6bcbf9b4-9546-42d3-965a-de055ab85475")]
3544    struct ScriptSendingMessages {
3545        index: u32,
3546    }
3547
3548    impl ScriptTrait for ScriptSendingMessages {
3549        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3550            match self.index {
3551                0 => ctx.message_sender.send_global(MyMessage::Foo(123)),
3552                1 => ctx
3553                    .message_sender
3554                    .send_global(MyMessage::Bar("Foobar".to_string())),
3555                _ => (),
3556            }
3557            self.index += 1;
3558
3559            Ok(())
3560        }
3561    }
3562
3563    #[test]
3564    fn test_messages() {
3565        let resource_manager =
3566            ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3567        let mut scene = Scene::new();
3568
3569        let (tx, rx) = mpsc::channel();
3570
3571        PivotBuilder::new(BaseBuilder::new().with_script(ScriptSendingMessages { index: 0 }))
3572            .build(&mut scene.graph);
3573
3574        let receiver_messages =
3575            PivotBuilder::new(BaseBuilder::new().with_script(ScriptListeningToMessages {
3576                sender: tx,
3577                index: 0,
3578            }))
3579            .build(&mut scene.graph)
3580            .to_base();
3581        let receiver_messages_source = Source {
3582            node_handle: receiver_messages,
3583            script_index: 0,
3584        };
3585
3586        let mut scene_container = SceneContainer::new(Default::default());
3587
3588        let scene_handle = scene_container.add(scene);
3589
3590        let mut script_processor = ScriptProcessor::default();
3591        let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3592        let mut gc = GraphicsContext::Uninitialized(Default::default());
3593        let mut user_interfaces = UiContainer::default();
3594
3595        script_processor.register_scripted_scene(scene_handle, &resource_manager);
3596
3597        for iteration in 0..2 {
3598            script_processor.handle_scripts(
3599                &mut scene_container,
3600                &mut Vec::new(),
3601                &resource_manager,
3602                &mut task_pool,
3603                &mut gc,
3604                &mut user_interfaces,
3605                0.0,
3606                0.0,
3607                &Default::default(),
3608                &mut Default::default(),
3609            );
3610
3611            match iteration {
3612                0 => {
3613                    assert_eq!(
3614                        rx.try_recv(),
3615                        Ok(Event::EventReceived(receiver_messages_source))
3616                    );
3617                    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3618                }
3619                1 => {
3620                    assert_eq!(
3621                        rx.try_recv(),
3622                        Ok(Event::EventReceived(receiver_messages_source))
3623                    );
3624                    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3625                }
3626                _ => (),
3627            }
3628        }
3629    }
3630
3631    #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3632    #[type_uuid(id = "7bcbf9b4-9546-42d3-965a-de055ab85475")]
3633    pub struct ScriptSpawningAsyncTasks {
3634        num: Option<u32>,
3635    }
3636
3637    impl ScriptTrait for ScriptSpawningAsyncTasks {
3638        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3639            ctx.task_pool.spawn_script_task(
3640                ctx.scene_handle,
3641                ctx.handle,
3642                ctx.script_index,
3643                async move { 123u32 },
3644                |result, script: &mut ScriptSpawningAsyncTasks, _ctx| {
3645                    assert_eq!(result, 123u32);
3646                    script.num = Some(result);
3647                    Ok(())
3648                },
3649            );
3650            Ok(())
3651        }
3652    }
3653
3654    #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3655    #[type_uuid(id = "8bcbf9b4-9546-42d3-965a-de055ab85475")]
3656    pub struct ScriptWithoutAsyncTasks {}
3657
3658    impl ScriptTrait for ScriptWithoutAsyncTasks {}
3659
3660    #[test]
3661    #[cfg(not(target_os = "macos"))] // This fails on macOS for some reason.
3662    fn test_async_script_tasks() {
3663        use crate::engine::{Engine, EngineInitParams};
3664
3665        let task_pool = Arc::new(TaskPool::default());
3666        let mut engine = Engine::new(EngineInitParams {
3667            graphics_context_params: Default::default(),
3668            serialization_context: Arc::new(Default::default()),
3669            widget_constructors: Arc::new(Default::default()),
3670            dyn_type_constructors: Arc::new(Default::default()),
3671            resource_manager: ResourceManager::new(Arc::new(FsResourceIo), task_pool.clone()),
3672            task_pool,
3673        })
3674        .unwrap();
3675
3676        let is_running = Cell::new(true);
3677
3678        engine.enable_plugins(
3679            None,
3680            true,
3681            ApplicationLoopController::Headless {
3682                running: &is_running,
3683            },
3684        );
3685
3686        let mut scene = Scene::new();
3687
3688        let handle = PivotBuilder::new(
3689            BaseBuilder::new()
3690                .with_script(ScriptSpawningAsyncTasks { num: None })
3691                .with_script(ScriptWithoutAsyncTasks {}),
3692        )
3693        .build(&mut scene.graph);
3694
3695        let scene_handle = engine.scenes.add(scene);
3696
3697        engine.register_scripted_scene(scene_handle);
3698
3699        // Spin for some time.
3700        let mut time = 0.0;
3701        let dt = 1.0 / 60.0;
3702        let mut lag = 0.0;
3703        while time <= 10.0 {
3704            engine.update(
3705                dt,
3706                ApplicationLoopController::Headless {
3707                    running: &is_running,
3708                },
3709                &mut lag,
3710                Default::default(),
3711            );
3712            time += dt;
3713        }
3714
3715        // Ensure that the tasks are finished and correctly handled.
3716        let mut scripts = engine.scenes[scene_handle].graph[handle].scripts();
3717        assert_eq!(
3718            scripts
3719                .next()
3720                .and_then(|s| s.cast::<ScriptSpawningAsyncTasks>()),
3721            Some(&ScriptSpawningAsyncTasks { num: Some(123) })
3722        );
3723        assert_eq!(
3724            scripts
3725                .next()
3726                .and_then(|s| s.cast::<ScriptWithoutAsyncTasks>()),
3727            Some(&ScriptWithoutAsyncTasks {})
3728        );
3729    }
3730
3731    #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3732    #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3733    pub struct ScriptThatDeletesItself {
3734        #[reflect(hidden)]
3735        #[visit(skip)]
3736        sender: Sender<Event>,
3737    }
3738
3739    impl ScriptTrait for ScriptThatDeletesItself {
3740        fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3741            self.sender
3742                .send(Event::Initialized(Source::from_ctx(ctx)))
3743                .unwrap();
3744            Ok(())
3745        }
3746
3747        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3748            self.sender
3749                .send(Event::Started(Source::from_ctx(ctx)))
3750                .unwrap();
3751            Ok(())
3752        }
3753
3754        fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3755            self.sender
3756                .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3757                .unwrap();
3758            Ok(())
3759        }
3760
3761        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3762            self.sender
3763                .send(Event::Updated(Source::from_ctx(ctx)))
3764                .unwrap();
3765
3766            let node = &mut ctx.scene.graph[ctx.handle];
3767            node.remove_script(ctx.script_index);
3768            Ok(())
3769        }
3770    }
3771
3772    #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3773    #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3774    pub struct ScriptThatAddsScripts {
3775        num: usize,
3776        #[reflect(hidden)]
3777        #[visit(skip)]
3778        sender: Sender<Event>,
3779    }
3780
3781    impl ScriptTrait for ScriptThatAddsScripts {
3782        fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3783            self.sender
3784                .send(Event::Initialized(Source::from_ctx(ctx)))
3785                .unwrap();
3786            Ok(())
3787        }
3788
3789        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3790            self.sender
3791                .send(Event::Started(Source::from_ctx(ctx)))
3792                .unwrap();
3793
3794            for i in 0..self.num {
3795                ctx.scene.graph[ctx.handle].add_script(SimpleScript {
3796                    stuff: i,
3797                    sender: self.sender.clone(),
3798                });
3799            }
3800
3801            Ok(())
3802        }
3803
3804        fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3805            self.sender
3806                .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3807                .unwrap();
3808
3809            Ok(())
3810        }
3811
3812        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3813            self.sender
3814                .send(Event::Updated(Source::from_ctx(ctx)))
3815                .unwrap();
3816
3817            Ok(())
3818        }
3819    }
3820
3821    #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3822    #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3823    pub struct SimpleScript {
3824        stuff: usize,
3825        #[reflect(hidden)]
3826        #[visit(skip)]
3827        sender: Sender<Event>,
3828    }
3829
3830    impl ScriptTrait for SimpleScript {
3831        fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3832            self.sender
3833                .send(Event::Initialized(Source::from_ctx(ctx)))
3834                .unwrap();
3835            Ok(())
3836        }
3837
3838        fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3839            self.sender
3840                .send(Event::Started(Source::from_ctx(ctx)))
3841                .unwrap();
3842            Ok(())
3843        }
3844
3845        fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3846            self.sender
3847                .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3848                .unwrap();
3849            Ok(())
3850        }
3851
3852        fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3853            self.sender
3854                .send(Event::Updated(Source::from_ctx(ctx)))
3855                .unwrap();
3856            Ok(())
3857        }
3858    }
3859
3860    #[test]
3861    fn test_script_adding_removing() {
3862        let resource_manager =
3863            ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3864        let mut scene = Scene::new();
3865
3866        let (tx, rx) = mpsc::channel();
3867
3868        let node_handle = PivotBuilder::new(
3869            BaseBuilder::new()
3870                .with_script(ScriptThatDeletesItself { sender: tx.clone() })
3871                .with_script(ScriptThatAddsScripts { num: 2, sender: tx }),
3872        )
3873        .build(&mut scene.graph)
3874        .to_base();
3875        assert_eq!(node_handle, Handle::<Node>::new(1, 1));
3876
3877        let mut scene_container = SceneContainer::new(Default::default());
3878
3879        let scene_handle = scene_container.add(scene);
3880
3881        let mut script_processor = ScriptProcessor::default();
3882
3883        script_processor.register_scripted_scene(scene_handle, &resource_manager);
3884
3885        let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3886        let mut gc = GraphicsContext::Uninitialized(Default::default());
3887        let mut user_interfaces = UiContainer::default();
3888
3889        for iteration in 0..2 {
3890            script_processor.handle_scripts(
3891                &mut scene_container,
3892                &mut Vec::new(),
3893                &resource_manager,
3894                &mut task_pool,
3895                &mut gc,
3896                &mut user_interfaces,
3897                0.0,
3898                0.0,
3899                &Default::default(),
3900                &mut Default::default(),
3901            );
3902
3903            match iteration {
3904                0 => {
3905                    for i in 0..2 {
3906                        assert_eq!(
3907                            rx.try_recv(),
3908                            Ok(Event::Initialized(Source {
3909                                node_handle,
3910                                script_index: i,
3911                            }))
3912                        );
3913                    }
3914                    for i in 0..2 {
3915                        assert_eq!(
3916                            rx.try_recv(),
3917                            Ok(Event::Started(Source {
3918                                node_handle,
3919                                script_index: i,
3920                            }))
3921                        );
3922                    }
3923                    for i in 2..4 {
3924                        assert_eq!(
3925                            rx.try_recv(),
3926                            Ok(Event::Initialized(Source {
3927                                node_handle,
3928                                script_index: i,
3929                            }))
3930                        );
3931                    }
3932                    for i in 2..4 {
3933                        assert_eq!(
3934                            rx.try_recv(),
3935                            Ok(Event::Started(Source {
3936                                node_handle,
3937                                script_index: i,
3938                            }))
3939                        );
3940                    }
3941                    for i in 0..4 {
3942                        assert_eq!(
3943                            rx.try_recv(),
3944                            Ok(Event::Updated(Source {
3945                                node_handle,
3946                                script_index: i,
3947                            }))
3948                        );
3949                    }
3950                    assert_eq!(
3951                        rx.try_recv(),
3952                        Ok(Event::Destroyed(Source {
3953                            node_handle,
3954                            script_index: 0,
3955                        }))
3956                    );
3957
3958                    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3959                }
3960                1 => {
3961                    for i in 0..3 {
3962                        assert_eq!(
3963                            rx.try_recv(),
3964                            Ok(Event::Updated(Source {
3965                                node_handle,
3966                                script_index: i,
3967                            }))
3968                        );
3969                    }
3970
3971                    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3972                }
3973                _ => (),
3974            }
3975        }
3976    }
3977}