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