fyrox_impl/engine/
mod.rs

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