1#![warn(missing_docs)]
25
26pub mod error;
27pub mod executor;
28pub mod input;
29pub mod task;
30
31mod hotreload;
32mod wasm_utils;
33
34use crate::engine::input::InputState;
35use crate::renderer::ui_renderer::UiRenderInfo;
36use crate::scene::skybox::SkyBoxKind;
37use crate::{
38 asset::{
39 event::ResourceEvent,
40 manager::{ResourceManager, ResourceWaitContext},
41 state::ResourceState,
42 untyped::{ResourceKind, UntypedResource},
43 },
44 core::{
45 algebra::Vector2,
46 futures::{executor::block_on, future::join_all},
47 instant,
48 log::Log,
49 pool::Handle,
50 reflect::Reflect,
51 task::TaskPool,
52 variable::try_inherit_properties,
53 SafeLock,
54 },
55 engine::{error::EngineError, task::TaskPoolHandler},
56 event::Event,
57 graph::{BaseSceneGraph, NodeMapping, SceneGraph},
58 graphics::error::FrameworkError,
59 gui::{
60 constructor::WidgetConstructorContainer,
61 font::{loader::FontLoader, Font, BUILT_IN_FONT},
62 loader::UserInterfaceLoader,
63 style::{self, resource::StyleLoader, Style},
64 UiContainer, UiUpdateSwitches, UserInterface,
65 },
66 material::{
67 self,
68 loader::MaterialLoader,
69 shader::{loader::ShaderLoader, Shader, ShaderResource, ShaderResourceExtension},
70 Material,
71 },
72 plugin::{
73 dylib::DyLibDynamicPlugin, DynamicPlugin, Plugin, PluginContainer, PluginContext,
74 PluginRegistrationContext,
75 },
76 renderer::Renderer,
77 resource::{
78 curve::{loader::CurveLoader, CurveResourceState},
79 model::{loader::ModelLoader, Model, ModelResource},
80 texture::{
81 self, loader::TextureLoader, CompressionOptions, Texture, TextureImportOptions,
82 TextureKind, TextureMinificationFilter, TextureResource, TextureResourceExtension,
83 },
84 },
85 scene::{
86 base::NodeScriptMessage,
87 graph::{GraphUpdateSwitches, NodePool},
88 mesh::surface::{self, SurfaceData, SurfaceDataLoader},
89 navmesh,
90 node::{
91 constructor::{new_node_constructor_container, NodeConstructorContainer},
92 Node,
93 },
94 sound::SoundEngine,
95 tilemap::{
96 brush::{TileMapBrush, TileMapBrushLoader},
97 tileset::{TileSet, TileSetLoader},
98 CustomTileCollider, TileMapData,
99 },
100 Scene, SceneContainer, SceneLoader,
101 },
102 script::{
103 constructor::ScriptConstructorContainer, DynamicTypeId, PluginsRefMut, RoutingStrategy,
104 Script, ScriptContext, ScriptDeinitContext, ScriptMessage, ScriptMessageContext,
105 ScriptMessageKind, ScriptMessageSender, UniversalScriptContext,
106 },
107 window::Window,
108};
109use fxhash::{FxHashMap, FxHashSet};
110use fyrox_animation::AnimationTracksData;
111use fyrox_core::visitor::error::VisitError;
112use fyrox_core::warn;
113use fyrox_graphics::server::SharedGraphicsServer;
114use fyrox_graphics_gl::server::GlGraphicsServer;
115use fyrox_sound::{
116 buffer::{loader::SoundBufferLoader, SoundBuffer},
117 renderer::hrtf::{HrirSphereLoader, HrirSphereResourceData},
118};
119use fyrox_ui::RenderMode;
120use std::{
121 any::TypeId,
122 cell::Cell,
123 collections::{HashSet, VecDeque},
124 fmt::{Display, Formatter},
125 io::Cursor,
126 ops::{Deref, DerefMut},
127 path::{Path, PathBuf},
128 rc::Rc,
129 sync::{
130 mpsc::{channel, Receiver, Sender},
131 Arc,
132 },
133 time::Duration,
134};
135use uuid::Uuid;
136use winit::event::{DeviceEvent, ElementState, WindowEvent};
137use winit::event_loop::{ActiveEventLoop, EventLoop};
138use winit::{
139 dpi::{Position, Size},
140 window::Icon,
141 window::WindowAttributes,
142};
143
144pub struct SerializationContext {
147 pub node_constructors: NodeConstructorContainer,
149 pub script_constructors: ScriptConstructorContainer,
151}
152
153impl Default for SerializationContext {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159impl SerializationContext {
160 pub fn new() -> Self {
162 Self {
163 node_constructors: new_node_constructor_container(),
164 script_constructors: ScriptConstructorContainer::new(),
165 }
166 }
167}
168
169#[derive(Debug, Default)]
171pub struct PerformanceStatistics {
172 pub ui_time: Duration,
174
175 pub scripts_time: Duration,
177
178 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
192pub struct InitializedGraphicsContext {
194 pub window: Window,
196
197 pub renderer: Renderer,
199
200 params: GraphicsContextParams,
201}
202
203impl InitializedGraphicsContext {
204 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 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#[allow(clippy::large_enum_variant)]
248pub enum GraphicsContext {
249 Initialized(InitializedGraphicsContext),
251
252 Uninitialized(GraphicsContextParams),
254}
255
256impl GraphicsContext {
257 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 pub fn as_initialized_mut(&mut self) -> &mut InitializedGraphicsContext {
270 if let GraphicsContext::Initialized(ctx) = self {
271 ctx
272 } else {
273 panic!("Graphics context is uninitialized!")
274 }
275 }
276}
277
278struct SceneLoadingOptions {
279 derived: bool,
280}
281
282pub struct AsyncSceneLoader {
349 resource_manager: ResourceManager,
350 serialization_context: Arc<SerializationContext>,
351 receiver: Receiver<SceneLoadingResult>,
352 sender: Sender<SceneLoadingResult>,
353 loading_scenes: FxHashMap<PathBuf, LoadingScene>,
354}
355
356struct LoadingScene {
357 reported: bool,
358 path: PathBuf,
359 options: SceneLoadingOptions,
360}
361
362struct SceneLoadingResult {
363 uuid: Uuid,
364 path: PathBuf,
365 result: Result<(Scene, Vec<u8>), VisitError>,
366}
367
368impl AsyncSceneLoader {
369 fn new(
370 resource_manager: ResourceManager,
371 serialization_context: Arc<SerializationContext>,
372 ) -> Self {
373 let (sender, receiver) = channel();
374 Self {
375 resource_manager,
376 serialization_context,
377 receiver,
378 sender,
379 loading_scenes: Default::default(),
380 }
381 }
382
383 fn request_with_options<P: AsRef<Path>>(&mut self, path: P, opts: SceneLoadingOptions) {
384 let path = path.as_ref().to_path_buf();
385
386 if self.loading_scenes.contains_key(&path) {
387 Log::warn(format!("A scene {} is already loading!", path.display()))
388 } else {
389 self.loading_scenes.insert(
391 path.clone(),
392 LoadingScene {
393 reported: false,
394 path: path.clone(),
395 options: opts,
396 },
397 );
398
399 let sender = self.sender.clone();
401 let serialization_context = self.serialization_context.clone();
402 let resource_manager = self.resource_manager.clone();
403 let uuid = resource_manager.find::<Model>(&path).resource_uuid();
404
405 let io = resource_manager.resource_io();
407
408 let future = async move {
409 match SceneLoader::from_file(
410 path.clone(),
411 io.as_ref(),
412 serialization_context,
413 resource_manager.clone(),
414 )
415 .await
416 {
417 Ok((loader, data)) => {
418 let scene = loader.finish().await;
419 Log::verify(sender.send(SceneLoadingResult {
420 uuid,
421 path,
422 result: Ok((scene, data)),
423 }));
424 }
425 Err(e) => {
426 Log::verify(sender.send(SceneLoadingResult {
427 uuid,
428 path,
429 result: Err(e),
430 }));
431 }
432 }
433 };
434
435 #[cfg(not(target_arch = "wasm32"))]
436 {
437 std::thread::spawn(move || block_on(future));
438 }
439
440 #[cfg(target_arch = "wasm32")]
441 {
442 crate::core::wasm_bindgen_futures::spawn_local(future);
443 }
444 }
445 }
446
447 pub fn request<P: AsRef<Path>>(&mut self, path: P) {
459 self.request_with_options(path, SceneLoadingOptions { derived: true });
460 }
461
462 pub fn request_raw<P: AsRef<Path>>(&mut self, path: P) {
464 self.request_with_options(path, SceneLoadingOptions { derived: false });
465 }
466}
467
468pub struct Engine {
470 pub graphics_context: GraphicsContext,
472
473 pub resource_manager: ResourceManager,
478
479 pub user_interfaces: UiContainer,
481
482 pub scenes: SceneContainer,
484
485 pub async_scene_loader: AsyncSceneLoader,
487
488 pub task_pool: TaskPoolHandler,
490
491 performance_statistics: PerformanceStatistics,
492
493 model_events_receiver: Receiver<ResourceEvent>,
494
495 #[allow(dead_code)] sound_engine: SoundEngine,
497
498 plugins: Vec<PluginContainer>,
500
501 plugins_enabled: bool,
502
503 elapsed_time: f32,
505
506 input_state: InputState,
507
508 pub serialization_context: Arc<SerializationContext>,
511
512 pub widget_constructors: Arc<WidgetConstructorContainer>,
514
515 pub script_processor: ScriptProcessor,
517}
518
519#[derive(Debug, Hash, PartialEq, Eq)]
520enum MessageTypeId {
521 Static(TypeId),
522 Dynamic(DynamicTypeId),
523}
524
525pub struct ScriptMessageDispatcher {
527 type_groups: FxHashMap<MessageTypeId, FxHashSet<Handle<Node>>>,
528 message_receiver: Receiver<ScriptMessage>,
529}
530
531impl ScriptMessageDispatcher {
532 fn new(message_receiver: Receiver<ScriptMessage>) -> Self {
533 Self {
534 type_groups: Default::default(),
535 message_receiver,
536 }
537 }
538
539 pub fn subscribe_to<T: 'static>(&mut self, receiver: Handle<Node>) {
542 self.subscribe_internal_to(receiver, MessageTypeId::Static(TypeId::of::<T>()));
543 }
544
545 pub fn subscribe_dynamic_to(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
548 self.subscribe_internal_to(receiver, MessageTypeId::Dynamic(type_id));
549 }
550
551 #[inline]
552 fn subscribe_internal_to(&mut self, receiver: Handle<Node>, type_id: MessageTypeId) {
553 self.type_groups
554 .entry(type_id)
555 .and_modify(|v| {
556 v.insert(receiver);
557 })
558 .or_insert_with(|| FxHashSet::from_iter([receiver]));
559 }
560
561 pub fn unsubscribe_from<T: 'static>(&mut self, receiver: Handle<Node>) {
563 self.unsubscribe_internal_from(receiver, &MessageTypeId::Static(TypeId::of::<T>()));
564 }
565
566 pub fn unsubscribe_dynamic_from(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
568 self.unsubscribe_internal_from(receiver, &MessageTypeId::Dynamic(type_id));
569 }
570
571 #[inline]
572 fn unsubscribe_internal_from(&mut self, receiver: Handle<Node>, type_id: &MessageTypeId) {
573 if let Some(group) = self.type_groups.get_mut(type_id) {
574 group.remove(&receiver);
575 }
576 }
577
578 pub fn unsubscribe(&mut self, receiver: Handle<Node>) {
580 for group in self.type_groups.values_mut() {
581 group.remove(&receiver);
582 }
583 }
584
585 fn dispatch_messages(
586 &self,
587 scene: &mut Scene,
588 scene_handle: Handle<Scene>,
589 plugins: &mut [PluginContainer],
590 resource_manager: &ResourceManager,
591 dt: f32,
592 elapsed_time: f32,
593 message_sender: &ScriptMessageSender,
594 user_interfaces: &mut UiContainer,
595 graphics_context: &mut GraphicsContext,
596 task_pool: &mut TaskPoolHandler,
597 input_state: &InputState,
598 ) {
599 while let Ok(message) = self.message_receiver.try_recv() {
600 let type_id = match message.payload.get_dynamic_type_id() {
601 Some(it) => MessageTypeId::Dynamic(it),
602 None => MessageTypeId::Static(message.payload.deref().type_id()),
603 };
604 let receivers = self.type_groups.get(&type_id);
605
606 if receivers.is_none_or(|r| r.is_empty()) {
607 Log::warn(format!(
608 "Script message {message:?} was sent, but there's no receivers. \
609 Did you forgot to subscribe your script to the message?"
610 ));
611 }
612
613 if let Some(receivers) = receivers {
614 let mut payload = message.payload;
615
616 match message.kind {
617 ScriptMessageKind::Targeted(target) => {
618 if receivers.contains(&target) {
619 let mut context = ScriptMessageContext {
620 dt,
621 elapsed_time,
622 plugins: PluginsRefMut(plugins),
623 handle: target,
624 scene,
625 scene_handle,
626 resource_manager,
627 message_sender,
628 task_pool,
629 graphics_context,
630 user_interfaces,
631 script_index: 0,
632 input_state,
633 };
634
635 process_node_scripts(&mut context, &mut |s, ctx| {
636 s.on_message(&mut *payload, ctx)
637 })
638 }
639 }
640 ScriptMessageKind::Hierarchical { root, routing } => match routing {
641 RoutingStrategy::Up => {
642 let mut node = root;
643 while let Some(node_ref) = scene.graph.try_get_node(node) {
644 let parent = node_ref.parent();
645
646 let mut context = ScriptMessageContext {
647 dt,
648 elapsed_time,
649 plugins: PluginsRefMut(plugins),
650 handle: node,
651 scene,
652 scene_handle,
653 resource_manager,
654 message_sender,
655 task_pool,
656 graphics_context,
657 user_interfaces,
658 script_index: 0,
659 input_state,
660 };
661
662 if receivers.contains(&node) {
663 process_node_scripts(&mut context, &mut |s, ctx| {
664 s.on_message(&mut *payload, ctx)
665 });
666 }
667
668 node = parent;
669 }
670 }
671 RoutingStrategy::Down => {
672 for node in scene.graph.traverse_handle_iter(root).collect::<Vec<_>>() {
673 let mut context = ScriptMessageContext {
674 dt,
675 elapsed_time,
676 plugins: PluginsRefMut(plugins),
677 handle: node,
678 scene,
679 scene_handle,
680 resource_manager,
681 message_sender,
682 task_pool,
683 graphics_context,
684 user_interfaces,
685 script_index: 0,
686 input_state,
687 };
688
689 if receivers.contains(&node) {
690 process_node_scripts(&mut context, &mut |s, ctx| {
691 s.on_message(&mut *payload, ctx)
692 });
693 }
694 }
695 }
696 },
697 ScriptMessageKind::Global => {
698 for &node in receivers {
699 let mut context = ScriptMessageContext {
700 dt,
701 elapsed_time,
702 plugins: PluginsRefMut(plugins),
703 handle: node,
704 scene,
705 scene_handle,
706 resource_manager,
707 message_sender,
708 task_pool,
709 graphics_context,
710 user_interfaces,
711 script_index: 0,
712 input_state,
713 };
714
715 process_node_scripts(&mut context, &mut |s, ctx| {
716 s.on_message(&mut *payload, ctx)
717 });
718 }
719 }
720 }
721 }
722 }
723 }
724}
725
726pub struct ScriptedScene {
728 pub handle: Handle<Scene>,
730 pub message_sender: ScriptMessageSender,
732 message_dispatcher: ScriptMessageDispatcher,
733}
734
735#[derive(Default)]
737pub struct ScriptProcessor {
738 wait_list: Vec<ResourceWaitContext>,
739 pub scripted_scenes: Vec<ScriptedScene>,
741}
742
743impl ScriptProcessor {
744 fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
745 self.scripted_scenes.iter().any(|s| s.handle == scene)
746 }
747
748 fn register_scripted_scene(
749 &mut self,
750 scene: Handle<Scene>,
751 resource_manager: &ResourceManager,
752 ) {
753 assert!(!self.has_scripted_scene(scene));
755
756 let (tx, rx) = channel();
757 self.scripted_scenes.push(ScriptedScene {
758 handle: scene,
759 message_sender: ScriptMessageSender { sender: tx },
760 message_dispatcher: ScriptMessageDispatcher::new(rx),
761 });
762
763 self.wait_list
764 .push(resource_manager.state().get_wait_context());
765 }
766
767 fn handle_scripts(
768 &mut self,
769 scenes: &mut SceneContainer,
770 plugins: &mut [PluginContainer],
771 resource_manager: &ResourceManager,
772 task_pool: &mut TaskPoolHandler,
773 graphics_context: &mut GraphicsContext,
774 user_interfaces: &mut UiContainer,
775 dt: f32,
776 elapsed_time: f32,
777 input_state: &InputState,
778 ) {
779 self.wait_list
780 .retain_mut(|context| !context.is_all_loaded());
781
782 if !self.wait_list.is_empty() {
783 return;
784 }
785
786 self.scripted_scenes
787 .retain(|s| scenes.is_valid_handle(s.handle));
788
789 'scene_loop: for scripted_scene in self.scripted_scenes.iter_mut() {
790 let scene = &mut scenes[scripted_scene.handle];
791
792 if !*scene.enabled {
794 continue 'scene_loop;
795 }
796
797 let mut update_queue = VecDeque::new();
799 let mut start_queue = VecDeque::new();
800 let script_message_sender = scene.graph.script_message_sender.clone();
801 for (handle, node) in scene.graph.pair_iter_mut() {
802 node.scripts
804 .retain(|e| e.script.is_some() && !e.should_be_deleted);
805
806 if node.is_globally_enabled() {
807 for (i, entry) in node.scripts.iter().enumerate() {
808 if let Some(script) = entry.script.as_ref() {
809 if script.initialized {
810 if script.started {
811 update_queue.push_back((handle, i));
812 } else {
813 start_queue.push_back((handle, i));
814 }
815 } else {
816 script_message_sender
817 .send(NodeScriptMessage::InitializeScript {
818 handle,
819 script_index: i,
820 })
821 .unwrap();
822 }
823 }
824 }
825 }
826 }
827
828 let mut destruction_queue = VecDeque::new();
831
832 let max_iterations = 64;
833
834 'update_loop: for update_loop_iteration in 0..max_iterations {
835 let mut context = ScriptContext {
836 dt,
837 elapsed_time,
838 plugins: PluginsRefMut(plugins),
839 handle: Default::default(),
840 scene,
841 scene_handle: scripted_scene.handle,
842 resource_manager,
843 message_sender: &scripted_scene.message_sender,
844 message_dispatcher: &mut scripted_scene.message_dispatcher,
845 task_pool,
846 graphics_context,
847 user_interfaces,
848 script_index: 0,
849 input_state,
850 };
851
852 'init_loop: for init_loop_iteration in 0..max_iterations {
853 while let Ok(event) = context.scene.graph.script_message_receiver.try_recv() {
856 match event {
857 NodeScriptMessage::InitializeScript {
858 handle,
859 script_index,
860 } => {
861 context.handle = handle;
862 context.script_index = script_index;
863
864 process_node_script(
865 script_index,
866 &mut context,
867 &mut |script, context| {
868 if !script.initialized {
869 script.on_init(context);
870 script.initialized = true;
871 }
872
873 start_queue.push_back((handle, script_index));
875 },
876 );
877 }
878 NodeScriptMessage::DestroyScript {
879 handle,
880 script,
881 script_index,
882 } => {
883 destruction_queue.push_back((handle, script, script_index));
885 }
886 }
887 }
888
889 if start_queue.is_empty() {
890 break 'init_loop;
892 } else {
893 while let Some((handle, script_index)) = start_queue.pop_front() {
897 context.handle = handle;
898 context.script_index = script_index;
899
900 process_node_script(
901 script_index,
902 &mut context,
903 &mut |script, context| {
904 if script.initialized && !script.started {
905 script.on_start(context);
906 script.started = true;
907
908 update_queue.push_back((handle, script_index));
909 }
910 },
911 );
912 }
913 }
914
915 if init_loop_iteration == max_iterations - 1 {
916 Log::warn(
917 "Infinite init loop detected! Most likely some of \
918 your scripts causing infinite prefab instantiation!",
919 )
920 }
921 }
922
923 if update_queue.is_empty() {
925 break 'update_loop;
926 } else {
927 while let Some((handle, script_index)) = update_queue.pop_front() {
928 context.handle = handle;
929 context.script_index = script_index;
930
931 process_node_script(script_index, &mut context, &mut |script, context| {
932 script.on_update(context);
933 });
934 }
935 }
936
937 if update_loop_iteration == max_iterations - 1 {
938 Log::warn(
939 "Infinite update loop detected! Most likely some of \
940 your scripts causing infinite prefab instantiation!",
941 )
942 }
943 }
944
945 scripted_scene.message_dispatcher.dispatch_messages(
950 scene,
951 scripted_scene.handle,
952 plugins,
953 resource_manager,
954 dt,
955 elapsed_time,
956 &scripted_scene.message_sender,
957 user_interfaces,
958 graphics_context,
959 task_pool,
960 input_state,
961 );
962
963 let mut context = ScriptDeinitContext {
965 elapsed_time,
966 plugins: PluginsRefMut(plugins),
967 resource_manager,
968 scene,
969 scene_handle: scripted_scene.handle,
970 node_handle: Default::default(),
971 message_sender: &scripted_scene.message_sender,
972 user_interfaces,
973 graphics_context,
974 task_pool,
975 script_index: 0,
976 input_state,
977 };
978 while let Some((handle, mut script, index)) = destruction_queue.pop_front() {
979 context.node_handle = handle;
980 context.script_index = index;
981
982 scripted_scene.message_dispatcher.unsubscribe(handle);
984
985 script.on_deinit(&mut context);
988 }
989 }
990
991 for (handle, mut detached_scene) in scenes.destruction_list.drain(..) {
993 if let Some(scripted_scene) = self.scripted_scenes.iter().find(|s| s.handle == handle) {
994 let mut context = ScriptDeinitContext {
995 elapsed_time,
996 plugins: PluginsRefMut(plugins),
997 resource_manager,
998 scene: &mut detached_scene,
999 scene_handle: scripted_scene.handle,
1000 node_handle: Default::default(),
1001 message_sender: &scripted_scene.message_sender,
1002 task_pool,
1003 graphics_context,
1004 user_interfaces,
1005 script_index: 0,
1006 input_state,
1007 };
1008
1009 for node_index in 0..context.scene.graph.capacity() {
1011 let handle_node = context.scene.graph.handle_from_index(node_index);
1012 context.node_handle = handle_node;
1013
1014 process_node_scripts(&mut context, &mut |script, context| {
1015 if script.initialized {
1016 script.on_deinit(context)
1017 }
1018 });
1019 }
1020 }
1021 }
1022 }
1023}
1024
1025struct ResourceGraphVertex {
1026 resource: ModelResource,
1027 children: Vec<ResourceGraphVertex>,
1028}
1029
1030impl ResourceGraphVertex {
1031 pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1032 let mut children = Vec::new();
1033
1034 let mut dependent_resources = HashSet::new();
1036 for resource in resource_manager.state().iter() {
1037 if let Some(other_model) = resource.try_cast::<Model>() {
1038 let mut state = other_model.state();
1039 if let Some(model_data) = state.data() {
1040 if model_data
1041 .get_scene()
1042 .graph
1043 .linear_iter()
1044 .any(|n| n.resource.as_ref() == Some(&model))
1045 {
1046 dependent_resources.insert(other_model.clone());
1047 }
1048 }
1049 }
1050 }
1051
1052 children.extend(
1053 dependent_resources
1054 .into_iter()
1055 .map(|r| ResourceGraphVertex::new(r, resource_manager.clone())),
1056 );
1057
1058 Self {
1059 resource: model,
1060 children,
1061 }
1062 }
1063
1064 pub fn resolve(&self) {
1065 Log::info(format!(
1066 "Resolving {} resource from dependency graph...",
1067 self.resource.resource_uuid()
1068 ));
1069
1070 if block_on(self.resource.clone()).is_ok() {
1072 self.resource.data_ref().get_scene_mut();
1073
1074 for child in self.children.iter() {
1075 child.resolve();
1076 }
1077 }
1078 }
1079}
1080
1081struct ResourceDependencyGraph {
1082 root: ResourceGraphVertex,
1083}
1084
1085impl ResourceDependencyGraph {
1086 pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1087 Self {
1088 root: ResourceGraphVertex::new(model, resource_manager),
1089 }
1090 }
1091
1092 pub fn resolve(&self) {
1093 self.root.resolve()
1094 }
1095}
1096
1097pub type GraphicsServerConstructorResult = Result<(Window, SharedGraphicsServer), FrameworkError>;
1099
1100pub type GraphicsServerConstructorCallback = dyn Fn(
1105 &GraphicsContextParams,
1106 &ActiveEventLoop,
1107 WindowAttributes,
1108 bool,
1109) -> GraphicsServerConstructorResult;
1110
1111#[derive(Clone)]
1114pub struct GraphicsServerConstructor(Rc<GraphicsServerConstructorCallback>);
1115
1116impl Default for GraphicsServerConstructor {
1117 fn default() -> Self {
1118 Self(Rc::new(
1119 |params, window_target, window_builder, named_objects| {
1120 GlGraphicsServer::new(
1121 params.vsync,
1122 params.msaa_sample_count,
1123 window_target,
1124 window_builder,
1125 named_objects,
1126 )
1127 },
1128 ))
1129 }
1130}
1131
1132#[derive(Clone)]
1134pub struct GraphicsContextParams {
1135 pub window_attributes: WindowAttributes,
1137
1138 pub vsync: bool,
1141
1142 pub msaa_sample_count: Option<u8>,
1145
1146 pub graphics_server_constructor: GraphicsServerConstructor,
1148
1149 pub named_objects: bool,
1153}
1154
1155impl Default for GraphicsContextParams {
1156 fn default() -> Self {
1157 Self {
1158 window_attributes: Default::default(),
1159 vsync: true,
1160 msaa_sample_count: None,
1161 graphics_server_constructor: Default::default(),
1162 named_objects: false,
1163 }
1164 }
1165}
1166
1167pub struct EngineInitParams {
1169 pub graphics_context_params: GraphicsContextParams,
1174 pub serialization_context: Arc<SerializationContext>,
1176 pub widget_constructors: Arc<WidgetConstructorContainer>,
1178 pub resource_manager: ResourceManager,
1180 pub task_pool: Arc<TaskPool>,
1182}
1183
1184fn process_node_script<T, C>(index: usize, context: &mut C, func: &mut T) -> bool
1185where
1186 T: FnMut(&mut Script, &mut C),
1187 C: UniversalScriptContext,
1188{
1189 let Some(node) = context.node() else {
1190 return false;
1192 };
1193
1194 if !node.is_globally_enabled() {
1195 return false;
1196 }
1197
1198 let Some(entry) = node.scripts.get_mut(index) else {
1199 return false;
1201 };
1202
1203 let Some(mut script) = entry.take() else {
1204 return false;
1205 };
1206
1207 func(&mut script, context);
1208
1209 match context.node() {
1210 Some(node) => {
1211 let entry = node
1212 .scripts
1213 .get_mut(index)
1214 .expect("Scripts array cannot be modified!");
1215
1216 if entry.should_be_deleted {
1217 context.destroy_script_deferred(script, index);
1218 } else {
1219 entry.script = Some(script);
1221 }
1222 }
1223 None => {
1224 context.destroy_script_deferred(script, index);
1227 }
1228 }
1229
1230 true
1231}
1232
1233fn process_node_scripts<T, C>(context: &mut C, func: &mut T)
1234where
1235 T: FnMut(&mut Script, &mut C),
1236 C: UniversalScriptContext,
1237{
1238 let mut index = 0;
1239 loop {
1240 context.set_script_index(index);
1241
1242 if !process_node_script(index, context, func) {
1243 return;
1244 }
1245
1246 index += 1;
1248 }
1249}
1250
1251pub(crate) fn process_scripts<T>(
1252 scene: &mut Scene,
1253 scene_handle: Handle<Scene>,
1254 plugins: &mut [PluginContainer],
1255 resource_manager: &ResourceManager,
1256 message_sender: &ScriptMessageSender,
1257 message_dispatcher: &mut ScriptMessageDispatcher,
1258 task_pool: &mut TaskPoolHandler,
1259 graphics_context: &mut GraphicsContext,
1260 user_interfaces: &mut UiContainer,
1261 dt: f32,
1262 elapsed_time: f32,
1263 input_state: &InputState,
1264 mut func: T,
1265) where
1266 T: FnMut(&mut Script, &mut ScriptContext),
1267{
1268 let mut context = ScriptContext {
1269 dt,
1270 elapsed_time,
1271 plugins: PluginsRefMut(plugins),
1272 handle: Default::default(),
1273 scene,
1274 scene_handle,
1275 resource_manager,
1276 message_sender,
1277 message_dispatcher,
1278 task_pool,
1279 graphics_context,
1280 user_interfaces,
1281 script_index: 0,
1282 input_state,
1283 };
1284
1285 for node_index in 0..context.scene.graph.capacity() {
1286 context.handle = context.scene.graph.handle_from_index(node_index);
1287
1288 process_node_scripts(&mut context, &mut func);
1289 }
1290}
1291
1292pub(crate) fn initialize_resource_manager_loaders(
1293 resource_manager: &ResourceManager,
1294 serialization_context: Arc<SerializationContext>,
1295) {
1296 let model_loader = ModelLoader {
1297 resource_manager: resource_manager.clone(),
1298 serialization_context,
1299 default_import_options: Default::default(),
1300 };
1301
1302 let mut state = resource_manager.state();
1303
1304 for shader in ShaderResource::standard_shaders() {
1305 state.built_in_resources.add((*shader).clone());
1306 }
1307
1308 for texture in SkyBoxKind::built_in_skybox_textures() {
1309 state.built_in_resources.add(texture.clone());
1310 }
1311
1312 state.built_in_resources.add(BUILT_IN_FONT.clone());
1313
1314 state.built_in_resources.add(texture::PLACEHOLDER.clone());
1315 state.built_in_resources.add(texture::PURE_COLOR.clone());
1316 state.built_in_resources.add(style::DEFAULT_STYLE.clone());
1317
1318 for material in [
1319 &*material::STANDARD,
1320 &*material::STANDARD_2D,
1321 &*material::STANDARD_SPRITE,
1322 &*material::STANDARD_TERRAIN,
1323 &*material::STANDARD_TWOSIDES,
1324 &*material::STANDARD_PARTICLE_SYSTEM,
1325 &*material::STANDARD_WIDGET,
1326 ] {
1327 state.built_in_resources.add(material.clone());
1328 }
1329
1330 for surface in [
1331 &*surface::CUBE,
1332 &*surface::QUAD,
1333 &*surface::CYLINDER,
1334 &*surface::SPHERE,
1335 &*surface::CONE,
1336 &*surface::TORUS,
1337 ] {
1338 state.built_in_resources.add(surface.clone());
1339 }
1340
1341 state.constructors_container.add::<Texture>();
1342 state.constructors_container.add::<Shader>();
1343 state.constructors_container.add::<Model>();
1344 state.constructors_container.add::<CurveResourceState>();
1345 state.constructors_container.add::<SoundBuffer>();
1346 state.constructors_container.add::<HrirSphereResourceData>();
1347 state.constructors_container.add::<Material>();
1348 state.constructors_container.add::<Font>();
1349 state.constructors_container.add::<UserInterface>();
1350 state.constructors_container.add::<SurfaceData>();
1351 state.constructors_container.add::<TileSet>();
1352 state.constructors_container.add::<TileMapBrush>();
1353 state.constructors_container.add::<TileMapData>();
1354 state.constructors_container.add::<CustomTileCollider>();
1355 state.constructors_container.add::<AnimationTracksData>();
1356 state.constructors_container.add::<Style>();
1357
1358 let mut loaders = state.loaders.safe_lock();
1359 let gltf_loader = super::resource::gltf::GltfLoader {
1360 resource_manager: resource_manager.clone(),
1361 default_import_options: Default::default(),
1362 };
1363 loaders.set(gltf_loader);
1364 loaders.set(model_loader);
1365 loaders.set(TextureLoader {
1366 default_import_options: Default::default(),
1367 });
1368 loaders.set(SoundBufferLoader {
1369 default_import_options: Default::default(),
1370 });
1371 loaders.set(ShaderLoader);
1372 loaders.set(CurveLoader);
1373 loaders.set(HrirSphereLoader);
1374 loaders.set(MaterialLoader {
1375 resource_manager: resource_manager.clone(),
1376 });
1377 loaders.set(FontLoader::default());
1378 loaders.set(UserInterfaceLoader {
1379 resource_manager: resource_manager.clone(),
1380 });
1381 loaders.set(SurfaceDataLoader {});
1382 loaders.set(TileSetLoader {
1383 resource_manager: resource_manager.clone(),
1384 });
1385 loaders.set(TileMapBrushLoader {
1386 resource_manager: resource_manager.clone(),
1387 });
1388 loaders.set(StyleLoader {
1389 resource_manager: resource_manager.clone(),
1390 });
1391}
1392
1393#[derive(Copy, Clone)]
1395pub enum ApplicationLoopController<'a> {
1396 Headless {
1399 running: &'a Cell<bool>,
1402 },
1403 ActiveEventLoop(&'a ActiveEventLoop),
1405 EventLoop(&'a EventLoop<()>),
1407}
1408
1409impl ApplicationLoopController<'_> {
1410 pub fn exit(&self) {
1412 match self {
1413 ApplicationLoopController::Headless { running } => running.set(false),
1414 ApplicationLoopController::ActiveEventLoop(event_loop) => event_loop.exit(),
1415 ApplicationLoopController::EventLoop(_) => {
1416 warn!("Can't exit the loop until it is activated!")
1417 }
1418 }
1419 }
1420}
1421
1422impl Engine {
1423 #[inline]
1467 #[allow(unused_variables)]
1468 pub fn new(params: EngineInitParams) -> Result<Self, EngineError> {
1469 let EngineInitParams {
1470 graphics_context_params,
1471 serialization_context,
1472 widget_constructors,
1473 resource_manager,
1474 task_pool,
1475 } = params;
1476
1477 #[cfg(target_arch = "wasm32")]
1478 wasm_utils::set_panic_hook();
1479
1480 initialize_resource_manager_loaders(&resource_manager, serialization_context.clone());
1481
1482 let (rx, tx) = channel();
1483 resource_manager.state().event_broadcaster.add(rx);
1484
1485 let sound_engine = SoundEngine::without_device();
1486
1487 let user_interfaces =
1488 UiContainer::new_with_ui(UserInterface::new(Vector2::new(100.0, 100.0)));
1489
1490 Ok(Self {
1491 graphics_context: GraphicsContext::Uninitialized(graphics_context_params),
1492 model_events_receiver: tx,
1493 async_scene_loader: AsyncSceneLoader::new(
1494 resource_manager.clone(),
1495 serialization_context.clone(),
1496 ),
1497 resource_manager,
1498 scenes: SceneContainer::new(sound_engine.clone()),
1499 sound_engine,
1500 user_interfaces,
1501 performance_statistics: Default::default(),
1502 plugins: Default::default(),
1503 serialization_context,
1504 widget_constructors,
1505 script_processor: Default::default(),
1506 plugins_enabled: false,
1507 elapsed_time: 0.0,
1508 task_pool: TaskPoolHandler::new(task_pool),
1509 input_state: Default::default(),
1510 })
1511 }
1512
1513 pub fn initialize_graphics_context(
1521 &mut self,
1522 event_loop: &ActiveEventLoop,
1523 ) -> Result<(), EngineError> {
1524 if let GraphicsContext::Uninitialized(params) = &self.graphics_context {
1525 let (window, server) = params.graphics_server_constructor.0(
1526 params,
1527 event_loop,
1528 params.window_attributes.clone(),
1529 params.named_objects,
1530 )?;
1531 let frame_size = (window.inner_size().width, window.inner_size().height);
1532
1533 let renderer = Renderer::new(server, frame_size, &self.resource_manager)?;
1534
1535 for ui in self.user_interfaces.iter_mut() {
1536 ui.set_screen_size(Vector2::new(frame_size.0 as f32, frame_size.1 as f32));
1537 }
1538
1539 self.graphics_context = GraphicsContext::Initialized(InitializedGraphicsContext {
1540 renderer,
1541 window,
1542 params: params.clone(),
1543 });
1544
1545 if let Err(err) = self.sound_engine.initialize_audio_output_device() {
1546 Log::err(format!(
1547 "Unable to initialize audio output device! Reason: {err:?}"
1548 ));
1549 }
1550
1551 Ok(())
1552 } else {
1553 Err(EngineError::Custom(
1554 "Graphics context is already initialized!".to_string(),
1555 ))
1556 }
1557 }
1558
1559 pub fn destroy_graphics_context(&mut self) -> Result<(), EngineError> {
1566 if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
1567 let params = &ctx.params;
1568 let window = &ctx.window;
1569
1570 let mut window_attributes = WindowAttributes::default();
1571
1572 window_attributes.inner_size = Some(Size::Physical(window.inner_size()));
1573 window_attributes.min_inner_size = params.window_attributes.min_inner_size;
1574 window_attributes.max_inner_size = params.window_attributes.max_inner_size;
1575 window_attributes.position = window.outer_position().ok().map(Position::Physical);
1576 window_attributes.resizable = window.is_resizable();
1577 window_attributes.enabled_buttons = window.enabled_buttons();
1578 window_attributes.title = window.title();
1579 window_attributes.maximized = window.is_maximized();
1580 window_attributes.visible = window.is_visible().unwrap_or(true);
1581 window_attributes.transparent = params.window_attributes.transparent;
1582 window_attributes.decorations = window.is_decorated();
1583 window_attributes.preferred_theme = params.window_attributes.preferred_theme;
1584 window_attributes.resize_increments = window.resize_increments().map(Size::Physical);
1585 window_attributes.content_protected = params.window_attributes.content_protected;
1586 window_attributes.window_level = params.window_attributes.window_level;
1587 window_attributes.active = params.window_attributes.active;
1588 window_attributes
1589 .window_icon
1590 .clone_from(¶ms.window_attributes.window_icon);
1591
1592 self.graphics_context = GraphicsContext::Uninitialized(GraphicsContextParams {
1593 window_attributes,
1594 vsync: params.vsync,
1595 msaa_sample_count: params.msaa_sample_count,
1596 graphics_server_constructor: params.graphics_server_constructor.clone(),
1597 named_objects: params.named_objects,
1598 });
1599
1600 self.sound_engine.destroy_audio_output_device();
1601
1602 Ok(())
1603 } else {
1604 Err(EngineError::Custom(
1605 "Graphics context is already destroyed!".to_string(),
1606 ))
1607 }
1608 }
1609
1610 pub fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
1613 if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
1614 ctx.renderer.set_frame_size(new_size)?;
1615 }
1616
1617 Ok(())
1618 }
1619
1620 pub fn elapsed_time(&self) -> f32 {
1624 self.elapsed_time
1625 }
1626
1627 pub fn update(
1641 &mut self,
1642 dt: f32,
1643 controller: ApplicationLoopController,
1644 lag: &mut f32,
1645 switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
1646 ) {
1647 self.handle_async_scene_loading(dt, lag, controller);
1648 self.pre_update(dt, controller, lag, switches);
1649 self.post_update(dt, &Default::default(), lag, controller);
1650 self.handle_plugins_hot_reloading(dt, controller, lag, |_| {});
1651 }
1652
1653 pub fn handle_plugins_hot_reloading<F>(
1661 &mut self,
1662 #[allow(unused_variables)] dt: f32,
1663 #[allow(unused_variables)] controller: ApplicationLoopController,
1664 #[allow(unused_variables)] lag: &mut f32,
1665 #[allow(unused_variables)] on_reloaded: F,
1666 ) where
1667 F: FnMut(&dyn Plugin),
1668 {
1669 #[cfg(any(unix, windows))]
1670 {
1671 if let Err(message) = self.reload_dynamic_plugins(dt, controller, lag, on_reloaded) {
1672 Log::err(format!(
1673 "Unable to reload dynamic plugins. Reason: {message}"
1674 ))
1675 }
1676 }
1677 }
1678
1679 fn handle_async_scene_loading(
1680 &mut self,
1681 dt: f32,
1682 lag: &mut f32,
1683 controller: ApplicationLoopController,
1684 ) {
1685 let len = self.async_scene_loader.loading_scenes.len();
1686 let mut n = 0;
1687 while n < len {
1688 if let Some(request) = self.async_scene_loader.loading_scenes.values_mut().nth(n) {
1689 if !request.reported {
1690 request.reported = true;
1691
1692 if self.plugins_enabled {
1694 let path = request.path.clone();
1695 let mut context = PluginContext {
1696 scenes: &mut self.scenes,
1697 resource_manager: &self.resource_manager,
1698 graphics_context: &mut self.graphics_context,
1699 dt,
1700 lag,
1701 user_interfaces: &mut self.user_interfaces,
1702 serialization_context: &self.serialization_context,
1703 widget_constructors: &self.widget_constructors,
1704 performance_statistics: &self.performance_statistics,
1705 elapsed_time: self.elapsed_time,
1706 script_processor: &self.script_processor,
1707 async_scene_loader: &mut self.async_scene_loader,
1708 loop_controller: controller,
1709 task_pool: &mut self.task_pool,
1710 input_state: &self.input_state,
1711 };
1712
1713 for plugin in self.plugins.iter_mut() {
1714 plugin.on_scene_begin_loading(&path, &mut context);
1715 }
1716 }
1717 }
1718 }
1719
1720 n += 1;
1721 }
1722
1723 while let Ok(loading_result) = self.async_scene_loader.receiver.try_recv() {
1724 if let Some(request) = self
1725 .async_scene_loader
1726 .loading_scenes
1727 .remove(&loading_result.path)
1728 {
1729 let mut context = PluginContext {
1730 scenes: &mut self.scenes,
1731 resource_manager: &self.resource_manager,
1732 graphics_context: &mut self.graphics_context,
1733 dt,
1734 lag,
1735 user_interfaces: &mut self.user_interfaces,
1736 serialization_context: &self.serialization_context,
1737 widget_constructors: &self.widget_constructors,
1738 performance_statistics: &self.performance_statistics,
1739 elapsed_time: self.elapsed_time,
1740 script_processor: &self.script_processor,
1741 async_scene_loader: &mut self.async_scene_loader,
1742 loop_controller: controller,
1743 task_pool: &mut self.task_pool,
1744 input_state: &self.input_state,
1745 };
1746
1747 match loading_result.result {
1748 Ok((mut scene, data)) => {
1749 if request.options.derived {
1750 let model = self
1751 .resource_manager
1752 .find_uuid::<Model>(loading_result.uuid);
1753 let data = Model {
1756 mapping: NodeMapping::UseHandles,
1757 scene: scene
1761 .clone_ex(
1762 scene.graph.get_root(),
1763 &mut |_, _| true,
1764 &mut |_, _| {},
1765 &mut |_, _, _| {},
1766 )
1767 .0,
1768 };
1769 model.header().state.commit_ok(data);
1770
1771 for (handle, node) in scene.graph.pair_iter_mut() {
1772 node.set_inheritance_data(handle, model.clone());
1773 }
1774
1775 (&mut scene as &mut dyn Reflect).apply_recursively_mut(
1778 &mut |object| {
1779 let type_id = (*object).type_id();
1780 if type_id != TypeId::of::<NodePool>() {
1781 object.as_inheritable_variable_mut(&mut |variable| {
1782 if let Some(variable) = variable {
1783 variable.reset_modified_flag();
1784 }
1785 });
1786 }
1787 },
1788 &[
1789 TypeId::of::<UntypedResource>(),
1790 TypeId::of::<navmesh::Container>(),
1791 ],
1792 )
1793 } else {
1794 if let Some(source_asset) =
1796 scene.graph[scene.graph.get_root()].root_resource()
1797 {
1798 let source_asset_ref = source_asset.data_ref();
1799 let source_scene_ref = &source_asset_ref.scene;
1800 Log::verify(try_inherit_properties(
1801 &mut scene,
1802 source_scene_ref,
1803 &[
1804 TypeId::of::<NodePool>(),
1805 TypeId::of::<UntypedResource>(),
1806 TypeId::of::<navmesh::Container>(),
1807 ],
1808 ));
1809 }
1810 }
1811
1812 let scene_handle = context.scenes.add(scene);
1813
1814 if self.plugins_enabled {
1816 for plugin in self.plugins.iter_mut() {
1817 Log::info(format!(
1818 "Scene {} was loaded successfully!",
1819 loading_result.path.display()
1820 ));
1821
1822 plugin.on_scene_loaded(
1823 &request.path,
1824 scene_handle,
1825 &data,
1826 &mut context,
1827 );
1828 }
1829 }
1830 }
1831 Err(error) => {
1832 if self.plugins_enabled {
1834 Log::err(format!(
1835 "Unable to load scene {:?}. Reason: {}",
1836 loading_result.path, error
1837 ));
1838
1839 for plugin in self.plugins.iter_mut() {
1840 plugin.on_scene_loading_failed(&request.path, &error, &mut context);
1841 }
1842 }
1843 }
1844 }
1845 }
1846 }
1847 }
1848
1849 pub fn pre_update(
1864 &mut self,
1865 dt: f32,
1866 controller: ApplicationLoopController,
1867 lag: &mut f32,
1868 switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
1869 ) {
1870 self.update_plugins(dt, controller, lag);
1873 self.handle_scripts(dt);
1874
1875 self.resource_manager.state().update(dt);
1879 self.handle_model_events();
1880
1881 let window_size = if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
1882 let inner_size = ctx.window.inner_size();
1883 let window_size = Vector2::new(inner_size.width as f32, inner_size.height as f32);
1884 ctx.renderer.update_caches(&self.resource_manager, dt);
1885 window_size
1886 } else {
1887 Vector2::new(1.0, 1.0)
1888 };
1889
1890 for (handle, scene) in self.scenes.pair_iter_mut().filter(|(_, s)| *s.enabled) {
1891 let frame_size =
1892 scene
1893 .rendering_options
1894 .render_target
1895 .as_ref()
1896 .map_or(window_size, |rt| {
1897 if let TextureKind::Rectangle { width, height } = rt.data_ref().kind() {
1898 Vector2::new(width as f32, height as f32)
1899 } else {
1900 panic!("only rectangle textures can be used as render target!");
1901 }
1902 });
1903
1904 scene.update(
1905 frame_size,
1906 dt,
1907 switches.get(&handle).cloned().unwrap_or_default(),
1908 );
1909 }
1910 }
1911
1912 pub fn post_update(
1917 &mut self,
1918 dt: f32,
1919 ui_update_switches: &UiUpdateSwitches,
1920 lag: &mut f32,
1921 controller: ApplicationLoopController,
1922 ) {
1923 if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
1924 let inner_size = ctx.window.inner_size();
1925 let window_size = Vector2::new(inner_size.width as f32, inner_size.height as f32);
1926
1927 let time = instant::Instant::now();
1928 for ui in self.user_interfaces.iter_mut() {
1929 ui.update(window_size, dt, ui_update_switches);
1930 }
1931 self.performance_statistics.ui_time = instant::Instant::now() - time;
1932 self.elapsed_time += dt;
1933
1934 self.post_update_plugins(dt, controller, lag);
1935
1936 self.input_state.mouse.speed = Vector2::default();
1937 self.input_state.keyboard.released_keys.clear();
1938 self.input_state.keyboard.pressed_keys.clear();
1939 }
1940 }
1941
1942 pub fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
1944 self.script_processor.has_scripted_scene(scene)
1945 }
1946
1947 pub fn register_scripted_scene(&mut self, scene: Handle<Scene>) {
1949 self.script_processor
1950 .register_scripted_scene(scene, &self.resource_manager)
1951 }
1952
1953 fn handle_scripts(&mut self, dt: f32) {
1954 let time = instant::Instant::now();
1955
1956 self.script_processor.handle_scripts(
1957 &mut self.scenes,
1958 &mut self.plugins,
1959 &self.resource_manager,
1960 &mut self.task_pool,
1961 &mut self.graphics_context,
1962 &mut self.user_interfaces,
1963 dt,
1964 self.elapsed_time,
1965 &self.input_state,
1966 );
1967
1968 self.performance_statistics.scripts_time = instant::Instant::now() - time;
1969 }
1970
1971 fn handle_async_tasks(
1972 &mut self,
1973 dt: f32,
1974 controller: ApplicationLoopController,
1975 lag: &mut f32,
1976 ) {
1977 while let Some(result) = self.task_pool.inner().next_task_result() {
1978 if let Some(plugin_task_handler) = self.task_pool.pop_plugin_task_handler(result.id) {
1979 (plugin_task_handler)(
1981 result.payload,
1982 &mut self.plugins,
1983 &mut PluginContext {
1984 scenes: &mut self.scenes,
1985 resource_manager: &self.resource_manager,
1986 graphics_context: &mut self.graphics_context,
1987 dt,
1988 lag,
1989 user_interfaces: &mut self.user_interfaces,
1990 serialization_context: &self.serialization_context,
1991 widget_constructors: &self.widget_constructors,
1992 performance_statistics: &self.performance_statistics,
1993 elapsed_time: self.elapsed_time,
1994 script_processor: &self.script_processor,
1995 async_scene_loader: &mut self.async_scene_loader,
1996 loop_controller: controller,
1997 task_pool: &mut self.task_pool,
1998 input_state: &self.input_state,
1999 },
2000 )
2001 } else if let Some(node_task_handler) = self.task_pool.pop_node_task_handler(result.id)
2002 {
2003 if let Some(scripted_scene) = self
2005 .script_processor
2006 .scripted_scenes
2007 .iter_mut()
2008 .find(|e| e.handle == node_task_handler.scene_handle)
2009 {
2010 let payload = result.payload;
2011 if let Some(scene) = self.scenes.try_get_mut(node_task_handler.scene_handle) {
2012 if let Some(node) =
2013 scene.graph.try_get_node_mut(node_task_handler.node_handle)
2014 {
2015 if let Some(mut script) = node
2016 .scripts
2017 .get_mut(node_task_handler.script_index)
2018 .and_then(|e| e.script.take())
2019 {
2020 (node_task_handler.closure)(
2021 payload,
2022 script.deref_mut(),
2023 &mut ScriptContext {
2024 dt,
2025 elapsed_time: self.elapsed_time,
2026 plugins: PluginsRefMut(&mut self.plugins),
2027 handle: node_task_handler.node_handle,
2028 scene,
2029 scene_handle: scripted_scene.handle,
2030 resource_manager: &self.resource_manager,
2031 message_sender: &scripted_scene.message_sender,
2032 message_dispatcher: &mut scripted_scene.message_dispatcher,
2033 task_pool: &mut self.task_pool,
2034 graphics_context: &mut self.graphics_context,
2035 user_interfaces: &mut self.user_interfaces,
2036 script_index: node_task_handler.script_index,
2037 input_state: &self.input_state,
2038 },
2039 );
2040
2041 if let Some(node) =
2042 scene.graph.try_get_node_mut(node_task_handler.node_handle)
2043 {
2044 if let Some(entry) =
2045 node.scripts.get_mut(node_task_handler.script_index)
2046 {
2047 if entry.should_be_deleted {
2048 Log::verify(scene.graph.script_message_sender.send(
2049 NodeScriptMessage::DestroyScript {
2050 script,
2051 handle: node_task_handler.node_handle,
2052 script_index: node_task_handler.script_index,
2053 },
2054 ));
2055 } else {
2056 entry.script = Some(script);
2057 }
2058 }
2059 }
2060 }
2061 }
2062 }
2063 }
2064 }
2065 }
2066 }
2067
2068 fn update_plugins(&mut self, dt: f32, controller: ApplicationLoopController, lag: &mut f32) {
2069 let time = instant::Instant::now();
2070
2071 if self.plugins_enabled {
2072 self.handle_async_tasks(dt, controller, lag);
2074
2075 let mut context = PluginContext {
2077 scenes: &mut self.scenes,
2078 resource_manager: &self.resource_manager,
2079 graphics_context: &mut self.graphics_context,
2080 dt,
2081 lag,
2082 user_interfaces: &mut self.user_interfaces,
2083 serialization_context: &self.serialization_context,
2084 widget_constructors: &self.widget_constructors,
2085 performance_statistics: &self.performance_statistics,
2086 elapsed_time: self.elapsed_time,
2087 script_processor: &self.script_processor,
2088 async_scene_loader: &mut self.async_scene_loader,
2089 loop_controller: controller,
2090 task_pool: &mut self.task_pool,
2091 input_state: &self.input_state,
2092 };
2093
2094 for plugin in self.plugins.iter_mut() {
2095 plugin.update(&mut context);
2096 }
2097
2098 let mut uis = self
2099 .user_interfaces
2100 .pair_iter()
2101 .map(|(h, _)| h)
2102 .collect::<VecDeque<_>>();
2103
2104 while let Some(ui) = uis.pop_front() {
2105 while let Some(message) = self
2106 .user_interfaces
2107 .try_get_mut(ui)
2108 .and_then(|ui| ui.poll_message())
2109 {
2110 let mut context = PluginContext {
2111 scenes: &mut self.scenes,
2112 resource_manager: &self.resource_manager,
2113 graphics_context: &mut self.graphics_context,
2114 dt,
2115 lag,
2116 user_interfaces: &mut self.user_interfaces,
2117 serialization_context: &self.serialization_context,
2118 widget_constructors: &self.widget_constructors,
2119 performance_statistics: &self.performance_statistics,
2120 elapsed_time: self.elapsed_time,
2121 script_processor: &self.script_processor,
2122 async_scene_loader: &mut self.async_scene_loader,
2123 loop_controller: controller,
2124 task_pool: &mut self.task_pool,
2125 input_state: &self.input_state,
2126 };
2127
2128 for plugin in self.plugins.iter_mut() {
2129 plugin.on_ui_message(&mut context, &message, ui);
2130 }
2131 }
2132 }
2133 }
2134
2135 self.performance_statistics.plugins_time = instant::Instant::now() - time;
2136 }
2137
2138 fn post_update_plugins(
2139 &mut self,
2140 dt: f32,
2141 controller: ApplicationLoopController,
2142 lag: &mut f32,
2143 ) {
2144 let time = instant::Instant::now();
2145
2146 if self.plugins_enabled {
2147 let mut context = PluginContext {
2148 scenes: &mut self.scenes,
2149 resource_manager: &self.resource_manager,
2150 graphics_context: &mut self.graphics_context,
2151 dt,
2152 lag,
2153 user_interfaces: &mut self.user_interfaces,
2154 serialization_context: &self.serialization_context,
2155 widget_constructors: &self.widget_constructors,
2156 performance_statistics: &self.performance_statistics,
2157 elapsed_time: self.elapsed_time,
2158 script_processor: &self.script_processor,
2159 async_scene_loader: &mut self.async_scene_loader,
2160 loop_controller: controller,
2161 task_pool: &mut self.task_pool,
2162 input_state: &self.input_state,
2163 };
2164
2165 for plugin in self.plugins.iter_mut() {
2166 plugin.post_update(&mut context);
2167 }
2168 }
2169
2170 self.performance_statistics.plugins_time += instant::Instant::now() - time;
2171 }
2172
2173 pub fn handle_os_events(
2175 &mut self,
2176 event: &Event<()>,
2177 dt: f32,
2178 controller: ApplicationLoopController,
2179 lag: &mut f32,
2180 ) {
2181 match event {
2182 Event::WindowEvent { event, .. } => match event {
2183 WindowEvent::KeyboardInput { event, .. } => {
2184 let keyboard = &mut self.input_state.keyboard;
2185
2186 match event.state {
2187 ElementState::Pressed => {
2188 if keyboard
2189 .keys
2190 .get(&event.physical_key)
2191 .is_none_or(|state| *state == ElementState::Released)
2192 {
2193 keyboard.pressed_keys.insert(event.physical_key);
2194 }
2195 }
2196 ElementState::Released => {
2197 if keyboard
2198 .keys
2199 .get(&event.physical_key)
2200 .is_some_and(|state| *state == ElementState::Pressed)
2201 {
2202 keyboard.released_keys.insert(event.physical_key);
2203 }
2204 }
2205 }
2206
2207 keyboard.keys.insert(event.physical_key, event.state);
2208 }
2209 WindowEvent::CursorMoved { position, .. } => {
2210 self.input_state.mouse.position =
2211 Vector2::new(position.x as f32, position.y as f32);
2212 }
2213 _ => (),
2214 },
2215 Event::DeviceEvent { event, .. } => match event {
2216 DeviceEvent::MouseMotion { delta } => {
2217 self.input_state.mouse.speed = Vector2::new(delta.0 as f32, delta.1 as f32);
2218 }
2219 DeviceEvent::Button { button, state } => {
2220 let mouse = &mut self.input_state.mouse;
2221
2222 match *state {
2223 ElementState::Pressed => {
2224 if mouse
2225 .buttons_state
2226 .get(button)
2227 .is_none_or(|state| *state == ElementState::Released)
2228 {
2229 mouse.pressed_buttons.insert(*button);
2230 }
2231 }
2232 ElementState::Released => {
2233 if mouse
2234 .buttons_state
2235 .get(button)
2236 .is_some_and(|state| *state == ElementState::Pressed)
2237 {
2238 mouse.released_buttons.insert(*button);
2239 }
2240 }
2241 }
2242
2243 mouse.buttons_state.insert(*button, *state);
2244 }
2245 _ => (),
2246 },
2247 _ => (),
2248 }
2249
2250 if self.plugins_enabled {
2251 for plugin in self.plugins.iter_mut() {
2252 plugin.on_os_event(
2253 event,
2254 PluginContext {
2255 scenes: &mut self.scenes,
2256 resource_manager: &self.resource_manager,
2257 graphics_context: &mut self.graphics_context,
2258 dt,
2259 lag,
2260 user_interfaces: &mut self.user_interfaces,
2261 serialization_context: &self.serialization_context,
2262 widget_constructors: &self.widget_constructors,
2263 performance_statistics: &self.performance_statistics,
2264 elapsed_time: self.elapsed_time,
2265 script_processor: &self.script_processor,
2266 async_scene_loader: &mut self.async_scene_loader,
2267 loop_controller: controller,
2268 task_pool: &mut self.task_pool,
2269 input_state: &self.input_state,
2270 },
2271 );
2272 }
2273 }
2274 }
2275
2276 pub(crate) fn handle_graphics_context_created_by_plugins(
2277 &mut self,
2278 dt: f32,
2279 controller: ApplicationLoopController,
2280 lag: &mut f32,
2281 ) {
2282 if self.plugins_enabled {
2283 for plugin in self.plugins.iter_mut() {
2284 plugin.on_graphics_context_initialized(PluginContext {
2285 scenes: &mut self.scenes,
2286 resource_manager: &self.resource_manager,
2287 graphics_context: &mut self.graphics_context,
2288 dt,
2289 lag,
2290 user_interfaces: &mut self.user_interfaces,
2291 serialization_context: &self.serialization_context,
2292 widget_constructors: &self.widget_constructors,
2293 performance_statistics: &self.performance_statistics,
2294 elapsed_time: self.elapsed_time,
2295 script_processor: &self.script_processor,
2296 async_scene_loader: &mut self.async_scene_loader,
2297 loop_controller: controller,
2298 task_pool: &mut self.task_pool,
2299 input_state: &self.input_state,
2300 });
2301 }
2302 }
2303 }
2304
2305 pub(crate) fn handle_graphics_context_destroyed_by_plugins(
2306 &mut self,
2307 dt: f32,
2308 controller: ApplicationLoopController,
2309 lag: &mut f32,
2310 ) {
2311 if self.plugins_enabled {
2312 for plugin in self.plugins.iter_mut() {
2313 plugin.on_graphics_context_destroyed(PluginContext {
2314 scenes: &mut self.scenes,
2315 resource_manager: &self.resource_manager,
2316 graphics_context: &mut self.graphics_context,
2317 dt,
2318 lag,
2319 user_interfaces: &mut self.user_interfaces,
2320 serialization_context: &self.serialization_context,
2321 widget_constructors: &self.widget_constructors,
2322 performance_statistics: &self.performance_statistics,
2323 elapsed_time: self.elapsed_time,
2324 script_processor: &self.script_processor,
2325 async_scene_loader: &mut self.async_scene_loader,
2326 loop_controller: controller,
2327 task_pool: &mut self.task_pool,
2328 input_state: &self.input_state,
2329 });
2330 }
2331 }
2332 }
2333
2334 pub(crate) fn handle_before_rendering_by_plugins(
2335 &mut self,
2336 dt: f32,
2337 controller: ApplicationLoopController,
2338 lag: &mut f32,
2339 ) {
2340 if self.plugins_enabled {
2341 for plugin in self.plugins.iter_mut() {
2342 plugin.before_rendering(PluginContext {
2343 scenes: &mut self.scenes,
2344 resource_manager: &self.resource_manager,
2345 graphics_context: &mut self.graphics_context,
2346 dt,
2347 lag,
2348 user_interfaces: &mut self.user_interfaces,
2349 serialization_context: &self.serialization_context,
2350 widget_constructors: &self.widget_constructors,
2351 performance_statistics: &self.performance_statistics,
2352 elapsed_time: self.elapsed_time,
2353 script_processor: &self.script_processor,
2354 async_scene_loader: &mut self.async_scene_loader,
2355 loop_controller: controller,
2356 task_pool: &mut self.task_pool,
2357 input_state: &self.input_state,
2358 });
2359 }
2360 }
2361 }
2362
2363 pub(crate) fn handle_os_event_by_scripts(
2371 &mut self,
2372 event: &Event<()>,
2373 scene_handle: Handle<Scene>,
2374 dt: f32,
2375 ) {
2376 if let Some(scripted_scene) = self
2377 .script_processor
2378 .scripted_scenes
2379 .iter_mut()
2380 .find(|s| s.handle == scene_handle)
2381 {
2382 let scene = &mut self.scenes[scene_handle];
2383 if *scene.enabled {
2384 process_scripts(
2385 scene,
2386 scene_handle,
2387 &mut self.plugins,
2388 &self.resource_manager,
2389 &scripted_scene.message_sender,
2390 &mut scripted_scene.message_dispatcher,
2391 &mut self.task_pool,
2392 &mut self.graphics_context,
2393 &mut self.user_interfaces,
2394 dt,
2395 self.elapsed_time,
2396 &self.input_state,
2397 |script, context| {
2398 if script.initialized && script.started {
2399 script.on_os_event(event, context);
2400 }
2401 },
2402 )
2403 }
2404 }
2405 }
2406
2407 pub fn handle_model_events(&mut self) {
2412 while let Ok(event) = self.model_events_receiver.try_recv() {
2413 if let ResourceEvent::Reloaded(resource) = event {
2414 if let Some(model) = resource.try_cast::<Model>() {
2415 Log::info(format!(
2416 "A model resource {} was reloaded, propagating changes...",
2417 model.resource_uuid()
2418 ));
2419
2420 ResourceDependencyGraph::new(model, self.resource_manager.clone()).resolve();
2422
2423 Log::info("Propagating changes to active scenes...");
2424
2425 for scene in self.scenes.iter_mut() {
2429 scene.resolve();
2430 }
2431 }
2432 }
2433 }
2434 }
2435
2436 #[inline]
2439 pub fn render(&mut self) -> Result<(), FrameworkError> {
2440 for ui in self.user_interfaces.iter_mut() {
2441 ui.set_time(self.elapsed_time);
2442 ui.draw();
2443 }
2444
2445 if let GraphicsContext::Initialized(ref mut ctx) = self.graphics_context {
2446 ctx.renderer.render_and_swap_buffers(
2447 &self.scenes,
2448 self.elapsed_time,
2449 self.user_interfaces
2450 .iter_mut()
2451 .filter(|ui| match ui.render_mode {
2452 RenderMode::EveryFrame => true,
2453 RenderMode::OnChanges => ui.need_render,
2454 })
2455 .map(|ui| {
2456 ui.need_render = false;
2457 UiRenderInfo {
2458 ui,
2459 render_target: ui.render_target.clone(),
2460 clear_color: Default::default(),
2461 resource_manager: &self.resource_manager,
2462 }
2463 }),
2464 &ctx.window,
2465 &self.resource_manager,
2466 )?;
2467 }
2468
2469 Ok(())
2470 }
2471
2472 pub(crate) fn enable_plugins(
2474 &mut self,
2475 scene_path: Option<&str>,
2476 enabled: bool,
2477 controller: ApplicationLoopController,
2478 ) {
2479 if self.plugins_enabled != enabled {
2480 self.plugins_enabled = enabled;
2481
2482 if self.plugins_enabled {
2483 for plugin in self.plugins.iter_mut() {
2485 plugin.init(
2486 scene_path,
2487 PluginContext {
2488 scenes: &mut self.scenes,
2489 resource_manager: &self.resource_manager,
2490 graphics_context: &mut self.graphics_context,
2491 dt: 0.0,
2492 lag: &mut 0.0,
2493 user_interfaces: &mut self.user_interfaces,
2494 serialization_context: &self.serialization_context,
2495 widget_constructors: &self.widget_constructors,
2496 performance_statistics: &self.performance_statistics,
2497 elapsed_time: self.elapsed_time,
2498 script_processor: &self.script_processor,
2499 async_scene_loader: &mut self.async_scene_loader,
2500 loop_controller: controller,
2501 task_pool: &mut self.task_pool,
2502 input_state: &self.input_state,
2503 },
2504 );
2505 }
2506 } else {
2507 self.handle_scripts(0.0);
2508
2509 for mut plugin in self.plugins.drain(..) {
2510 plugin.on_deinit(PluginContext {
2512 scenes: &mut self.scenes,
2513 resource_manager: &self.resource_manager,
2514 graphics_context: &mut self.graphics_context,
2515 dt: 0.0,
2516 lag: &mut 0.0,
2517 user_interfaces: &mut self.user_interfaces,
2518 serialization_context: &self.serialization_context,
2519 widget_constructors: &self.widget_constructors,
2520 performance_statistics: &self.performance_statistics,
2521 elapsed_time: self.elapsed_time,
2522 script_processor: &self.script_processor,
2523 async_scene_loader: &mut self.async_scene_loader,
2524 loop_controller: controller,
2525 task_pool: &mut self.task_pool,
2526 input_state: &self.input_state,
2527 });
2528 }
2529 }
2530 }
2531 }
2532
2533 fn register_plugin_internal(
2534 serialization_context: &Arc<SerializationContext>,
2535 widget_constructors: &Arc<WidgetConstructorContainer>,
2536 resource_manager: &ResourceManager,
2537 plugin: &dyn Plugin,
2538 ) {
2539 plugin.register(PluginRegistrationContext {
2540 serialization_context,
2541 widget_constructors,
2542 resource_manager,
2543 });
2544 }
2545
2546 fn register_plugin(&self, plugin: &dyn Plugin) {
2547 Self::register_plugin_internal(
2548 &self.serialization_context,
2549 &self.widget_constructors,
2550 &self.resource_manager,
2551 plugin,
2552 )
2553 }
2554
2555 pub fn add_plugin<P>(&mut self, plugin: P)
2557 where
2558 P: Plugin + 'static,
2559 {
2560 self.register_plugin(&plugin);
2561
2562 self.plugins.push(PluginContainer::Static(Box::new(plugin)));
2563 }
2564
2565 pub fn add_dynamic_plugin<P>(
2577 &mut self,
2578 path: P,
2579 reload_when_changed: bool,
2580 use_relative_paths: bool,
2581 ) -> Result<&dyn Plugin, String>
2582 where
2583 P: AsRef<Path> + 'static,
2584 {
2585 Ok(self.add_dynamic_plugin_custom(DyLibDynamicPlugin::new(
2586 path,
2587 reload_when_changed,
2588 use_relative_paths,
2589 )?))
2590 }
2591
2592 pub fn add_dynamic_plugin_custom<P>(&mut self, plugin: P) -> &dyn Plugin
2594 where
2595 P: DynamicPlugin + 'static,
2596 {
2597 let display_name = plugin.display_name();
2598
2599 let plugin_container = PluginContainer::Dynamic(Box::new(plugin));
2600
2601 self.register_plugin(plugin_container.deref());
2602 self.plugins.push(plugin_container);
2603
2604 Log::info(format!("Plugin {display_name:?} was loaded successfully"));
2605
2606 &**self.plugins.last().unwrap()
2607 }
2608
2609 pub fn reload_plugin(
2612 &mut self,
2613 plugin_index: usize,
2614 dt: f32,
2615 controller: ApplicationLoopController,
2616 lag: &mut f32,
2617 ) -> Result<(), String> {
2618 let plugin_container = &mut self.plugins[plugin_index];
2619 let PluginContainer::Dynamic(plugin) = plugin_container else {
2620 return Err(format!(
2621 "Plugin {plugin_index} is static and cannot be reloaded!",
2622 ));
2623 };
2624
2625 if !plugin.is_loaded() {
2626 return Err(format!("Cannot reload unloaded plugin {plugin_index}!"));
2630 }
2631 plugin.prepare_to_reload();
2632
2633 let plugin_type_id = plugin.as_loaded_ref().type_id();
2634 let plugin_assembly_name = plugin.as_loaded_ref().assembly_name();
2635
2636 let mut scenes_state = Vec::new();
2638 for (scene_handle, scene) in self.scenes.pair_iter_mut() {
2639 if let Some(data) = hotreload::SceneState::try_create_from_plugin(
2640 scene_handle,
2641 scene,
2642 &self.serialization_context,
2643 plugin.as_loaded_ref(),
2644 )? {
2645 scenes_state.push(data);
2646 }
2647 }
2648
2649 let mut prefab_scenes = Vec::new();
2651 let rm_state = self.resource_manager.state();
2652 for resource in rm_state.resources().iter() {
2653 if let Some(model) = resource.try_cast::<Model>() {
2654 let mut model_state = model.state();
2655 if let Some(data) = model_state.data() {
2656 if let Some(scene_state) = hotreload::SceneState::try_create_from_plugin(
2657 Handle::NONE,
2658 &mut data.scene,
2659 &self.serialization_context,
2660 plugin.as_loaded_ref(),
2661 )? {
2662 prefab_scenes.push((model.clone(), scene_state));
2663 }
2664 }
2665 }
2666 }
2667 drop(rm_state);
2668
2669 let mut constructors = FxHashSet::default();
2671 for (type_uuid, constructor) in self.serialization_context.script_constructors.map().iter()
2672 {
2673 if constructor.assembly_name == plugin_assembly_name {
2674 constructors.insert(*type_uuid);
2675 }
2676 }
2677 for type_uuid in constructors.iter() {
2678 self.serialization_context
2679 .script_constructors
2680 .remove(*type_uuid);
2681 }
2682
2683 let mut constructors = FxHashSet::default();
2685 for (type_uuid, constructor) in self.serialization_context.node_constructors.map().iter() {
2686 if constructor.assembly_name == plugin_assembly_name {
2687 constructors.insert(*type_uuid);
2688 }
2689 }
2690 for type_uuid in constructors.iter() {
2691 self.serialization_context
2692 .node_constructors
2693 .remove(*type_uuid);
2694 }
2695
2696 let mut constructors = FxHashSet::default();
2698 for (type_uuid, constructor) in self.widget_constructors.map().iter() {
2699 if constructor.assembly_name == plugin_assembly_name {
2700 constructors.insert(*type_uuid);
2701 }
2702 }
2703 for type_uuid in constructors.iter() {
2704 self.widget_constructors.remove(*type_uuid);
2705 }
2706
2707 {
2709 let mut resources_to_reload = FxHashSet::default();
2710 let mut state = self.resource_manager.state();
2711 for resource in state.resources().iter() {
2712 let data = resource.lock();
2713 if let ResourceState::Ok { ref data, .. } = data.state {
2714 data.as_reflect(&mut |reflect| {
2715 if reflect.assembly_name() == plugin_assembly_name {
2716 resources_to_reload.insert(resource.clone());
2717 }
2718 })
2719 }
2720 }
2721
2722 for resource_to_reload in resources_to_reload.iter() {
2723 Log::info(format!(
2724 "Reloading {:?} resource, because it is used in plugin {plugin_assembly_name}",
2725 state.resource_path(resource_to_reload)
2726 ));
2727
2728 state.reload_resource(resource_to_reload.clone());
2729 }
2730
2731 drop(state);
2732
2733 block_on(join_all(resources_to_reload));
2734 }
2735
2736 if let GraphicsContext::Initialized(ref mut graphics_context) = self.graphics_context {
2738 let render_passes = graphics_context.renderer.render_passes().to_vec();
2739 for render_pass in render_passes {
2740 if render_pass.borrow().source_type_id() == plugin_type_id {
2741 graphics_context.renderer.remove_render_pass(render_pass);
2742 }
2743 }
2744 }
2745
2746 let mut visitor = hotreload::make_writing_visitor();
2747 plugin
2748 .as_loaded_mut()
2749 .visit("Plugin", &mut visitor)
2750 .map_err(|e| e.to_string())?;
2751 let mut binary_blob = Cursor::new(Vec::<u8>::new());
2752 visitor
2753 .save_binary_to_memory(&mut binary_blob)
2754 .map_err(|e| e.to_string())?;
2755
2756 Log::info(format!(
2757 "Plugin {plugin_index} was serialized successfully!"
2758 ));
2759
2760 drop(visitor);
2765
2766 let binary_blob = binary_blob.into_inner();
2767
2768 plugin.reload(&mut |plugin| {
2769 Self::register_plugin_internal(
2773 &self.serialization_context,
2774 &self.widget_constructors,
2775 &self.resource_manager,
2776 plugin,
2777 );
2778
2779 self.resource_manager.update_or_load_registry();
2782
2783 let mut visitor = hotreload::make_reading_visitor(
2784 &binary_blob,
2785 &self.serialization_context,
2786 &self.resource_manager,
2787 &self.widget_constructors,
2788 )
2789 .map_err(|e| e.to_string())?;
2790
2791 plugin
2792 .visit("Plugin", &mut visitor)
2793 .map_err(|e| e.to_string())?;
2794 Ok(())
2795 })?;
2796
2797 for (model, scene_state) in prefab_scenes {
2799 Log::info(format!(
2800 "Deserializing {} prefab content...",
2801 model.resource_uuid()
2802 ));
2803
2804 scene_state.deserialize_into_prefab_scene(
2805 &model,
2806 &self.serialization_context,
2807 &self.resource_manager,
2808 &self.widget_constructors,
2809 )?;
2810 }
2811
2812 for scene_state in scenes_state {
2814 let scene = &mut self.scenes[scene_state.scene];
2815 scene_state.deserialize_into_scene(
2816 scene,
2817 &self.serialization_context,
2818 &self.resource_manager,
2819 &self.widget_constructors,
2820 )?;
2821 }
2822
2823 plugin.as_loaded_mut().on_loaded(PluginContext {
2825 scenes: &mut self.scenes,
2826 resource_manager: &self.resource_manager,
2827 user_interfaces: &mut self.user_interfaces,
2828 graphics_context: &mut self.graphics_context,
2829 dt,
2830 lag,
2831 serialization_context: &self.serialization_context,
2832 widget_constructors: &self.widget_constructors,
2833 performance_statistics: &Default::default(),
2834 elapsed_time: self.elapsed_time,
2835 script_processor: &self.script_processor,
2836 async_scene_loader: &mut self.async_scene_loader,
2837 loop_controller: controller,
2838 task_pool: &mut self.task_pool,
2839 input_state: &self.input_state,
2840 });
2841
2842 Log::info(format!("Plugin {plugin_index} was successfully reloaded!"));
2843
2844 Ok(())
2845 }
2846
2847 pub fn plugins(&self) -> &[PluginContainer] {
2849 &self.plugins
2850 }
2851
2852 pub fn plugins_mut(&mut self) -> &mut [PluginContainer] {
2854 &mut self.plugins
2855 }
2856
2857 pub fn reload_dynamic_plugins<F>(
2859 &mut self,
2860 dt: f32,
2861 controller: ApplicationLoopController,
2862 lag: &mut f32,
2863 mut on_reloaded: F,
2864 ) -> Result<(), String>
2865 where
2866 F: FnMut(&dyn Plugin),
2867 {
2868 for plugin_index in 0..self.plugins.len() {
2869 if let PluginContainer::Dynamic(plugin) = &self.plugins[plugin_index] {
2870 if plugin.is_reload_needed_now() {
2871 self.reload_plugin(plugin_index, dt, controller, lag)?;
2872
2873 on_reloaded(self.plugins[plugin_index].deref_mut());
2874 }
2875 }
2876 }
2877
2878 Ok(())
2879 }
2880}
2881
2882impl Drop for Engine {
2883 fn drop(&mut self) {
2884 let scenes = self
2888 .scenes
2889 .pair_iter()
2890 .map(|(h, _)| h)
2891 .collect::<Vec<Handle<Scene>>>();
2892
2893 for handle in scenes {
2894 self.scenes.remove(handle);
2895 }
2896
2897 self.enable_plugins(
2899 None,
2900 false,
2901 ApplicationLoopController::Headless {
2902 running: &Default::default(),
2903 },
2904 );
2905 }
2906}
2907
2908#[cfg(test)]
2909mod test {
2910 use crate::engine::ApplicationLoopController;
2911 use crate::{
2912 asset::manager::ResourceManager,
2913 core::{
2914 pool::Handle, reflect::prelude::*, task::TaskPool, type_traits::prelude::*,
2915 visitor::prelude::*,
2916 },
2917 engine::{task::TaskPoolHandler, GraphicsContext, ScriptProcessor},
2918 graph::BaseSceneGraph,
2919 scene::{base::BaseBuilder, node::Node, pivot::PivotBuilder, Scene, SceneContainer},
2920 script::{
2921 ScriptContext, ScriptDeinitContext, ScriptMessageContext, ScriptMessagePayload,
2922 ScriptTrait,
2923 },
2924 };
2925 use fyrox_resource::io::FsResourceIo;
2926 use fyrox_ui::UiContainer;
2927 use std::cell::Cell;
2928 use std::sync::{
2929 mpsc::{self, Sender, TryRecvError},
2930 Arc,
2931 };
2932
2933 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
2934 struct Source {
2935 node_handle: Handle<Node>,
2936 script_index: usize,
2937 }
2938
2939 impl Source {
2940 fn from_ctx(ctx: &ScriptContext) -> Self {
2941 Self {
2942 node_handle: ctx.handle,
2943 script_index: ctx.script_index,
2944 }
2945 }
2946
2947 fn from_deinit_ctx(ctx: &ScriptDeinitContext) -> Self {
2948 Self {
2949 node_handle: ctx.node_handle,
2950 script_index: ctx.script_index,
2951 }
2952 }
2953
2954 fn from_msg_ctx(ctx: &ScriptMessageContext) -> Self {
2955 Self {
2956 node_handle: ctx.handle,
2957 script_index: ctx.script_index,
2958 }
2959 }
2960 }
2961
2962 #[allow(clippy::enum_variant_names)]
2963 #[derive(PartialEq, Eq, Clone, Debug)]
2964 enum Event {
2965 Initialized(Source),
2966 Started(Source),
2967 Updated(Source),
2968 Destroyed(Source),
2969 EventReceived(Source),
2970 }
2971
2972 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
2973 #[type_uuid(id = "2569de84-d4b2-427d-969b-d5c7b31a0ba6")]
2974 struct MyScript {
2975 #[reflect(hidden)]
2976 #[visit(skip)]
2977 sender: Sender<Event>,
2978 spawned: bool,
2979 }
2980
2981 impl ScriptTrait for MyScript {
2982 fn on_init(&mut self, ctx: &mut ScriptContext) {
2983 self.sender
2984 .send(Event::Initialized(Source::from_ctx(ctx)))
2985 .unwrap();
2986
2987 let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
2989 sender: self.sender.clone(),
2990 }))
2991 .build(&mut ctx.scene.graph);
2992 assert_eq!(handle, Handle::new(2, 1));
2993 }
2994
2995 fn on_start(&mut self, ctx: &mut ScriptContext) {
2996 self.sender
2997 .send(Event::Started(Source::from_ctx(ctx)))
2998 .unwrap();
2999
3000 let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3002 sender: self.sender.clone(),
3003 }))
3004 .build(&mut ctx.scene.graph);
3005 assert_eq!(handle, Handle::new(3, 1));
3006 }
3007
3008 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) {
3009 self.sender
3010 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3011 .unwrap();
3012 }
3013
3014 fn on_update(&mut self, ctx: &mut ScriptContext) {
3015 self.sender
3016 .send(Event::Updated(Source::from_ctx(ctx)))
3017 .unwrap();
3018
3019 if !self.spawned {
3020 PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3022 sender: self.sender.clone(),
3023 }))
3024 .build(&mut ctx.scene.graph);
3025
3026 self.spawned = true;
3027 }
3028 }
3029 }
3030
3031 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3032 #[type_uuid(id = "1cebacd9-b500-4753-93be-39db344add21")]
3033 struct MySubScript {
3034 #[reflect(hidden)]
3035 #[visit(skip)]
3036 sender: Sender<Event>,
3037 }
3038
3039 impl ScriptTrait for MySubScript {
3040 fn on_init(&mut self, ctx: &mut ScriptContext) {
3041 self.sender
3042 .send(Event::Initialized(Source::from_ctx(ctx)))
3043 .unwrap();
3044 }
3045
3046 fn on_start(&mut self, ctx: &mut ScriptContext) {
3047 self.sender
3048 .send(Event::Started(Source::from_ctx(ctx)))
3049 .unwrap();
3050 }
3051
3052 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) {
3053 self.sender
3054 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3055 .unwrap();
3056 }
3057
3058 fn on_update(&mut self, ctx: &mut ScriptContext) {
3059 self.sender
3060 .send(Event::Updated(Source::from_ctx(ctx)))
3061 .unwrap();
3062 }
3063 }
3064
3065 #[test]
3066 fn test_order() {
3067 let resource_manager =
3068 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3069 let mut scene = Scene::new();
3070
3071 let (tx, rx) = mpsc::channel();
3072
3073 let node_handle = PivotBuilder::new(
3074 BaseBuilder::new()
3075 .with_script(MyScript {
3076 sender: tx.clone(),
3077 spawned: false,
3078 })
3079 .with_script(MySubScript { sender: tx }),
3080 )
3081 .build(&mut scene.graph);
3082 assert_eq!(node_handle, Handle::new(1, 1));
3083
3084 let node_handle_0 = Source {
3085 node_handle,
3086 script_index: 0,
3087 };
3088 let node_handle_1 = Source {
3089 node_handle,
3090 script_index: 1,
3091 };
3092
3093 let mut scene_container = SceneContainer::new(Default::default());
3094
3095 let scene_handle = scene_container.add(scene);
3096
3097 let mut script_processor = ScriptProcessor::default();
3098
3099 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3100
3101 let handle_on_init = Source {
3102 node_handle: Handle::new(2, 1),
3103 script_index: 0,
3104 };
3105 let handle_on_start = Source {
3106 node_handle: Handle::new(3, 1),
3107 script_index: 0,
3108 };
3109 let handle_on_update1 = Source {
3110 node_handle: Handle::new(4, 1),
3111 script_index: 0,
3112 };
3113 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3114 let mut gc = GraphicsContext::Uninitialized(Default::default());
3115 let mut user_interfaces = UiContainer::default();
3116
3117 for iteration in 0..3 {
3118 script_processor.handle_scripts(
3119 &mut scene_container,
3120 &mut Vec::new(),
3121 &resource_manager,
3122 &mut task_pool,
3123 &mut gc,
3124 &mut user_interfaces,
3125 0.0,
3126 0.0,
3127 &Default::default(),
3128 );
3129
3130 match iteration {
3131 0 => {
3132 assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_0)));
3133 assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_1)));
3134 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_init)));
3135 assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_0)));
3136 assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_1)));
3137 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_init)));
3138 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_start)));
3139 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_start)));
3140 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3141 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3142 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3143 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3144 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_update1)));
3145 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_update1)));
3146 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3147 }
3148 1 => {
3149 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3150 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3151 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3152 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3153 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3154 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3155
3156 let graph = &mut scene_container[scene_handle].graph;
3158 graph.remove_node(node_handle);
3159 graph.remove_node(handle_on_init.node_handle);
3160 graph.remove_node(handle_on_start.node_handle);
3161 graph.remove_node(handle_on_update1.node_handle);
3162 }
3163 2 => {
3164 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_0)));
3165 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_1)));
3166 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_init)));
3167 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_start)));
3168 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_update1)));
3169
3170 assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
3172 }
3173 _ => (),
3174 }
3175 }
3176 }
3177
3178 #[derive(Debug, ScriptMessagePayload)]
3179 enum MyMessage {
3180 Foo(usize),
3181 Bar(String),
3182 }
3183
3184 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3185 #[type_uuid(id = "bf2976ad-f41d-4de6-9a32-b1a293956058")]
3186 struct ScriptListeningToMessages {
3187 index: u32,
3188 #[reflect(hidden)]
3189 #[visit(skip)]
3190 sender: Sender<Event>,
3191 }
3192
3193 impl ScriptTrait for ScriptListeningToMessages {
3194 fn on_start(&mut self, ctx: &mut ScriptContext) {
3195 ctx.message_dispatcher.subscribe_to::<MyMessage>(ctx.handle);
3196 }
3197
3198 fn on_message(
3199 &mut self,
3200 message: &mut dyn ScriptMessagePayload,
3201 ctx: &mut ScriptMessageContext,
3202 ) {
3203 let typed_message = message.downcast_ref::<MyMessage>().unwrap();
3204 match self.index {
3205 0 => {
3206 if let MyMessage::Foo(num) = typed_message {
3207 assert_eq!(*num, 123);
3208 self.sender
3209 .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3210 .unwrap();
3211 } else {
3212 unreachable!()
3213 }
3214 }
3215 1 => {
3216 if let MyMessage::Bar(string) = typed_message {
3217 assert_eq!(string, "Foobar");
3218 self.sender
3219 .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3220 .unwrap();
3221 } else {
3222 unreachable!()
3223 }
3224 }
3225 _ => (),
3226 }
3227
3228 self.index += 1;
3229 }
3230 }
3231
3232 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3233 #[type_uuid(id = "6bcbf9b4-9546-42d3-965a-de055ab85475")]
3234 struct ScriptSendingMessages {
3235 index: u32,
3236 }
3237
3238 impl ScriptTrait for ScriptSendingMessages {
3239 fn on_update(&mut self, ctx: &mut ScriptContext) {
3240 match self.index {
3241 0 => ctx.message_sender.send_global(MyMessage::Foo(123)),
3242 1 => ctx
3243 .message_sender
3244 .send_global(MyMessage::Bar("Foobar".to_string())),
3245 _ => (),
3246 }
3247 self.index += 1;
3248 }
3249 }
3250
3251 #[test]
3252 fn test_messages() {
3253 let resource_manager =
3254 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3255 let mut scene = Scene::new();
3256
3257 let (tx, rx) = mpsc::channel();
3258
3259 PivotBuilder::new(BaseBuilder::new().with_script(ScriptSendingMessages { index: 0 }))
3260 .build(&mut scene.graph);
3261
3262 let receiver_messages =
3263 PivotBuilder::new(BaseBuilder::new().with_script(ScriptListeningToMessages {
3264 sender: tx,
3265 index: 0,
3266 }))
3267 .build(&mut scene.graph);
3268 let receiver_messages_source = Source {
3269 node_handle: receiver_messages,
3270 script_index: 0,
3271 };
3272
3273 let mut scene_container = SceneContainer::new(Default::default());
3274
3275 let scene_handle = scene_container.add(scene);
3276
3277 let mut script_processor = ScriptProcessor::default();
3278 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3279 let mut gc = GraphicsContext::Uninitialized(Default::default());
3280 let mut user_interfaces = UiContainer::default();
3281
3282 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3283
3284 for iteration in 0..2 {
3285 script_processor.handle_scripts(
3286 &mut scene_container,
3287 &mut Vec::new(),
3288 &resource_manager,
3289 &mut task_pool,
3290 &mut gc,
3291 &mut user_interfaces,
3292 0.0,
3293 0.0,
3294 &Default::default(),
3295 );
3296
3297 match iteration {
3298 0 => {
3299 assert_eq!(
3300 rx.try_recv(),
3301 Ok(Event::EventReceived(receiver_messages_source))
3302 );
3303 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3304 }
3305 1 => {
3306 assert_eq!(
3307 rx.try_recv(),
3308 Ok(Event::EventReceived(receiver_messages_source))
3309 );
3310 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3311 }
3312 _ => (),
3313 }
3314 }
3315 }
3316
3317 #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3318 #[type_uuid(id = "7bcbf9b4-9546-42d3-965a-de055ab85475")]
3319 pub struct ScriptSpawningAsyncTasks {
3320 num: Option<u32>,
3321 }
3322
3323 impl ScriptTrait for ScriptSpawningAsyncTasks {
3324 fn on_start(&mut self, ctx: &mut ScriptContext) {
3325 ctx.task_pool.spawn_script_task(
3326 ctx.scene_handle,
3327 ctx.handle,
3328 ctx.script_index,
3329 async move { 123u32 },
3330 |result, script: &mut ScriptSpawningAsyncTasks, _ctx| {
3331 assert_eq!(result, 123u32);
3332 script.num = Some(result);
3333 },
3334 )
3335 }
3336 }
3337
3338 #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3339 #[type_uuid(id = "8bcbf9b4-9546-42d3-965a-de055ab85475")]
3340 pub struct ScriptWithoutAsyncTasks {}
3341
3342 impl ScriptTrait for ScriptWithoutAsyncTasks {}
3343
3344 #[test]
3345 #[cfg(not(target_os = "macos"))] fn test_async_script_tasks() {
3347 use crate::engine::{Engine, EngineInitParams};
3348
3349 let task_pool = Arc::new(TaskPool::default());
3350 let mut engine = Engine::new(EngineInitParams {
3351 graphics_context_params: Default::default(),
3352 serialization_context: Arc::new(Default::default()),
3353 widget_constructors: Arc::new(Default::default()),
3354 resource_manager: ResourceManager::new(Arc::new(FsResourceIo), task_pool.clone()),
3355 task_pool,
3356 })
3357 .unwrap();
3358
3359 let is_running = Cell::new(true);
3360
3361 engine.enable_plugins(
3362 None,
3363 true,
3364 ApplicationLoopController::Headless {
3365 running: &is_running,
3366 },
3367 );
3368
3369 let mut scene = Scene::new();
3370
3371 let handle = PivotBuilder::new(
3372 BaseBuilder::new()
3373 .with_script(ScriptSpawningAsyncTasks { num: None })
3374 .with_script(ScriptWithoutAsyncTasks {}),
3375 )
3376 .build(&mut scene.graph);
3377
3378 let scene_handle = engine.scenes.add(scene);
3379
3380 engine.register_scripted_scene(scene_handle);
3381
3382 let mut time = 0.0;
3384 let dt = 1.0 / 60.0;
3385 let mut lag = 0.0;
3386 while time <= 10.0 {
3387 engine.update(
3388 dt,
3389 ApplicationLoopController::Headless {
3390 running: &is_running,
3391 },
3392 &mut lag,
3393 Default::default(),
3394 );
3395 time += dt;
3396 }
3397
3398 let mut scripts = engine.scenes[scene_handle].graph[handle].scripts();
3400 assert_eq!(
3401 scripts
3402 .next()
3403 .and_then(|s| s.cast::<ScriptSpawningAsyncTasks>()),
3404 Some(&ScriptSpawningAsyncTasks { num: Some(123) })
3405 );
3406 assert_eq!(
3407 scripts
3408 .next()
3409 .and_then(|s| s.cast::<ScriptWithoutAsyncTasks>()),
3410 Some(&ScriptWithoutAsyncTasks {})
3411 );
3412 }
3413
3414 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3415 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3416 pub struct ScriptThatDeletesItself {
3417 #[reflect(hidden)]
3418 #[visit(skip)]
3419 sender: Sender<Event>,
3420 }
3421
3422 impl ScriptTrait for ScriptThatDeletesItself {
3423 fn on_init(&mut self, ctx: &mut ScriptContext) {
3424 self.sender
3425 .send(Event::Initialized(Source::from_ctx(ctx)))
3426 .unwrap();
3427 }
3428
3429 fn on_start(&mut self, ctx: &mut ScriptContext) {
3430 self.sender
3431 .send(Event::Started(Source::from_ctx(ctx)))
3432 .unwrap();
3433 }
3434
3435 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) {
3436 self.sender
3437 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3438 .unwrap();
3439 }
3440
3441 fn on_update(&mut self, ctx: &mut ScriptContext) {
3442 self.sender
3443 .send(Event::Updated(Source::from_ctx(ctx)))
3444 .unwrap();
3445
3446 let node = &mut ctx.scene.graph[ctx.handle];
3447 node.remove_script(ctx.script_index);
3448 }
3449 }
3450
3451 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3452 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3453 pub struct ScriptThatAddsScripts {
3454 num: usize,
3455 #[reflect(hidden)]
3456 #[visit(skip)]
3457 sender: Sender<Event>,
3458 }
3459
3460 impl ScriptTrait for ScriptThatAddsScripts {
3461 fn on_init(&mut self, ctx: &mut ScriptContext) {
3462 self.sender
3463 .send(Event::Initialized(Source::from_ctx(ctx)))
3464 .unwrap();
3465 }
3466
3467 fn on_start(&mut self, ctx: &mut ScriptContext) {
3468 self.sender
3469 .send(Event::Started(Source::from_ctx(ctx)))
3470 .unwrap();
3471
3472 for i in 0..self.num {
3473 ctx.scene.graph[ctx.handle].add_script(SimpleScript {
3474 stuff: i,
3475 sender: self.sender.clone(),
3476 });
3477 }
3478 }
3479
3480 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) {
3481 self.sender
3482 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3483 .unwrap();
3484 }
3485
3486 fn on_update(&mut self, ctx: &mut ScriptContext) {
3487 self.sender
3488 .send(Event::Updated(Source::from_ctx(ctx)))
3489 .unwrap();
3490 }
3491 }
3492
3493 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3494 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3495 pub struct SimpleScript {
3496 stuff: usize,
3497 #[reflect(hidden)]
3498 #[visit(skip)]
3499 sender: Sender<Event>,
3500 }
3501
3502 impl ScriptTrait for SimpleScript {
3503 fn on_init(&mut self, ctx: &mut ScriptContext) {
3504 self.sender
3505 .send(Event::Initialized(Source::from_ctx(ctx)))
3506 .unwrap();
3507 }
3508
3509 fn on_start(&mut self, ctx: &mut ScriptContext) {
3510 self.sender
3511 .send(Event::Started(Source::from_ctx(ctx)))
3512 .unwrap();
3513 }
3514
3515 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) {
3516 self.sender
3517 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3518 .unwrap();
3519 }
3520
3521 fn on_update(&mut self, ctx: &mut ScriptContext) {
3522 self.sender
3523 .send(Event::Updated(Source::from_ctx(ctx)))
3524 .unwrap();
3525 }
3526 }
3527
3528 #[test]
3529 fn test_script_adding_removing() {
3530 let resource_manager =
3531 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3532 let mut scene = Scene::new();
3533
3534 let (tx, rx) = mpsc::channel();
3535
3536 let node_handle = PivotBuilder::new(
3537 BaseBuilder::new()
3538 .with_script(ScriptThatDeletesItself { sender: tx.clone() })
3539 .with_script(ScriptThatAddsScripts { num: 2, sender: tx }),
3540 )
3541 .build(&mut scene.graph);
3542 assert_eq!(node_handle, Handle::new(1, 1));
3543
3544 let mut scene_container = SceneContainer::new(Default::default());
3545
3546 let scene_handle = scene_container.add(scene);
3547
3548 let mut script_processor = ScriptProcessor::default();
3549
3550 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3551
3552 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3553 let mut gc = GraphicsContext::Uninitialized(Default::default());
3554 let mut user_interfaces = UiContainer::default();
3555
3556 for iteration in 0..2 {
3557 script_processor.handle_scripts(
3558 &mut scene_container,
3559 &mut Vec::new(),
3560 &resource_manager,
3561 &mut task_pool,
3562 &mut gc,
3563 &mut user_interfaces,
3564 0.0,
3565 0.0,
3566 &Default::default(),
3567 );
3568
3569 match iteration {
3570 0 => {
3571 for i in 0..2 {
3572 assert_eq!(
3573 rx.try_recv(),
3574 Ok(Event::Initialized(Source {
3575 node_handle,
3576 script_index: i,
3577 }))
3578 );
3579 }
3580 for i in 0..2 {
3581 assert_eq!(
3582 rx.try_recv(),
3583 Ok(Event::Started(Source {
3584 node_handle,
3585 script_index: i,
3586 }))
3587 );
3588 }
3589 for i in 2..4 {
3590 assert_eq!(
3591 rx.try_recv(),
3592 Ok(Event::Initialized(Source {
3593 node_handle,
3594 script_index: i,
3595 }))
3596 );
3597 }
3598 for i in 2..4 {
3599 assert_eq!(
3600 rx.try_recv(),
3601 Ok(Event::Started(Source {
3602 node_handle,
3603 script_index: i,
3604 }))
3605 );
3606 }
3607 for i in 0..4 {
3608 assert_eq!(
3609 rx.try_recv(),
3610 Ok(Event::Updated(Source {
3611 node_handle,
3612 script_index: i,
3613 }))
3614 );
3615 }
3616 assert_eq!(
3617 rx.try_recv(),
3618 Ok(Event::Destroyed(Source {
3619 node_handle,
3620 script_index: 0,
3621 }))
3622 );
3623
3624 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3625 }
3626 1 => {
3627 for i in 0..3 {
3628 assert_eq!(
3629 rx.try_recv(),
3630 Ok(Event::Updated(Source {
3631 node_handle,
3632 script_index: i,
3633 }))
3634 );
3635 }
3636
3637 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3638 }
3639 _ => (),
3640 }
3641 }
3642 }
3643}