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