Skip to main content

fyrox_impl/engine/
mod.rs

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