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::plugin::error::{GameError, GameResult};
35use crate::{
36 asset::{
37 event::ResourceEvent,
38 manager::{ResourceManager, ResourceWaitContext},
39 state::ResourceState,
40 untyped::{ResourceKind, UntypedResource},
41 },
42 core::{
43 algebra::Vector2,
44 err,
45 futures::{executor::block_on, future::join_all},
46 instant,
47 log::Log,
48 pool::Handle,
49 reflect::Reflect,
50 task::TaskPool,
51 variable::try_inherit_properties,
52 visitor::error::VisitError,
53 warn, SafeLock,
54 },
55 engine::{error::EngineError, input::InputState, task::TaskPoolHandler},
56 event::Event,
57 graph::{NodeMapping, SceneGraph},
58 graphics::error::FrameworkError,
59 gui::{
60 constructor::WidgetConstructorContainer,
61 font::{
62 loader::FontLoader, Font, BOLD_ITALIC, BUILT_IN_BOLD, BUILT_IN_FONT, BUILT_IN_ITALIC,
63 },
64 loader::UserInterfaceLoader,
65 style::{self, resource::StyleLoader, Style},
66 RenderMode, UiContainer, UiUpdateSwitches, UserInterface,
67 },
68 material::{
69 self,
70 loader::MaterialLoader,
71 shader::{loader::ShaderLoader, Shader, ShaderResource, ShaderResourceExtension},
72 Material,
73 },
74 plugin::{
75 dylib::DyLibDynamicPlugin, DynamicPlugin, Plugin, PluginContainer, PluginContext,
76 PluginRegistrationContext,
77 },
78 renderer::{ui_renderer::UiRenderInfo, Renderer},
79 resource::{
80 curve::{loader::CurveLoader, CurveResourceState},
81 gltf::material::GLTF_SHADER,
82 model::{loader::ModelLoader, Model, ModelResource},
83 texture::{
84 self, loader::TextureLoader, CompressionOptions, Texture, TextureImportOptions,
85 TextureKind, TextureMinificationFilter, TextureResource, TextureResourceExtension,
86 },
87 },
88 scene::{
89 base::NodeScriptMessage,
90 graph::{GraphUpdateSwitches, NodePool},
91 mesh::surface::{self, SurfaceData, SurfaceDataLoader},
92 navmesh,
93 node::{
94 constructor::{new_node_constructor_container, NodeConstructorContainer},
95 Node,
96 },
97 skybox::SkyBoxKind,
98 sound::SoundEngine,
99 tilemap::{
100 brush::{TileMapBrush, TileMapBrushLoader},
101 tileset::{TileSet, TileSetLoader},
102 CustomTileCollider, TileMapData,
103 },
104 Scene, SceneContainer, SceneLoader,
105 },
106 script::{
107 constructor::ScriptConstructorContainer, DynamicTypeId, PluginsRefMut, RoutingStrategy,
108 Script, ScriptContext, ScriptDeinitContext, ScriptMessage, ScriptMessageContext,
109 ScriptMessageKind, ScriptMessageSender, UniversalScriptContext,
110 },
111 window::Window,
112};
113use fxhash::{FxHashMap, FxHashSet};
114use fyrox_animation::AnimationTracksData;
115use fyrox_core::dyntype::DynTypeConstructorContainer;
116use fyrox_core::NameProvider;
117use fyrox_graphics::server::SharedGraphicsServer;
118use fyrox_graphics_gl::server::GlGraphicsServer;
119use fyrox_sound::{
120 buffer::{loader::SoundBufferLoader, SoundBuffer},
121 renderer::hrtf::{HrirSphereLoader, HrirSphereResourceData},
122};
123use std::{
124 any::TypeId,
125 cell::Cell,
126 collections::{HashSet, VecDeque},
127 fmt::{Display, Formatter},
128 io::Cursor,
129 ops::{Deref, DerefMut},
130 path::{Path, PathBuf},
131 rc::Rc,
132 sync::{
133 mpsc::{channel, Receiver, Sender},
134 Arc,
135 },
136 time::Duration,
137};
138use uuid::Uuid;
139use winit::event::{DeviceEvent, ElementState, WindowEvent};
140use winit::event_loop::{ActiveEventLoop, EventLoop};
141use winit::{
142 dpi::{Position, Size},
143 window::Icon,
144 window::WindowAttributes,
145};
146
147pub struct SerializationContext {
150 pub node_constructors: NodeConstructorContainer,
152 pub script_constructors: ScriptConstructorContainer,
154}
155
156impl Default for SerializationContext {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162impl SerializationContext {
163 pub fn new() -> Self {
165 Self {
166 node_constructors: new_node_constructor_container(),
167 script_constructors: ScriptConstructorContainer::new(),
168 }
169 }
170}
171
172#[derive(Debug, Default)]
174pub struct PerformanceStatistics {
175 pub ui_time: Duration,
177
178 pub scripts_time: Duration,
180
181 pub plugins_time: Duration,
183}
184
185impl Display for PerformanceStatistics {
186 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187 writeln!(
188 f,
189 "Performance Statistics:\n\tUI: {:?}\n\tScripts: {:?}\n\tPlugins: {:?}",
190 self.ui_time, self.scripts_time, self.plugins_time
191 )
192 }
193}
194
195pub struct InitializedGraphicsContext {
197 pub window: Window,
199
200 pub renderer: Renderer,
202
203 params: GraphicsContextParams,
204}
205
206impl InitializedGraphicsContext {
207 pub fn set_window_icon_from_memory(&mut self, data: &[u8]) {
211 if let Ok(texture) = TextureResource::load_from_memory(
212 Uuid::new_v4(),
213 ResourceKind::Embedded,
214 data,
215 TextureImportOptions::default()
216 .with_compression(CompressionOptions::NoCompression)
217 .with_minification_filter(TextureMinificationFilter::Linear),
218 ) {
219 self.set_window_icon_from_texture(&texture);
220 }
221 }
222
223 pub fn set_window_icon_from_texture(&mut self, texture: &TextureResource) {
225 let data = texture.data_ref();
226 if let TextureKind::Rectangle { width, height } = data.kind() {
227 if let Ok(img) = Icon::from_rgba(data.data().to_vec(), width, height) {
228 self.window.set_window_icon(Some(img));
229 }
230 }
231 }
232}
233
234#[allow(clippy::large_enum_variant)]
251pub enum GraphicsContext {
252 Initialized(InitializedGraphicsContext),
254
255 Uninitialized(GraphicsContextParams),
257}
258
259impl GraphicsContext {
260 pub fn as_initialized_ref(&self) -> &InitializedGraphicsContext {
263 if let GraphicsContext::Initialized(ctx) = self {
264 ctx
265 } else {
266 panic!("Graphics context is uninitialized!")
267 }
268 }
269
270 pub fn as_initialized_mut(&mut self) -> &mut InitializedGraphicsContext {
273 if let GraphicsContext::Initialized(ctx) = self {
274 ctx
275 } else {
276 panic!("Graphics context is uninitialized!")
277 }
278 }
279}
280
281struct SceneLoadingOptions {
282 derived: bool,
283}
284
285pub struct AsyncSceneLoader {
355 resource_manager: ResourceManager,
356 serialization_context: Arc<SerializationContext>,
357 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
358 receiver: Receiver<SceneLoadingResult>,
359 sender: Sender<SceneLoadingResult>,
360 loading_scenes: FxHashMap<PathBuf, LoadingScene>,
361}
362
363struct LoadingScene {
364 reported: bool,
365 path: PathBuf,
366 options: SceneLoadingOptions,
367}
368
369struct SceneLoadingResult {
370 uuid: Uuid,
371 path: PathBuf,
372 result: Result<(Scene, Vec<u8>), VisitError>,
373}
374
375impl AsyncSceneLoader {
376 fn new(
377 resource_manager: ResourceManager,
378 serialization_context: Arc<SerializationContext>,
379 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
380 ) -> Self {
381 let (sender, receiver) = channel();
382 Self {
383 resource_manager,
384 serialization_context,
385 dyn_type_constructors,
386 receiver,
387 sender,
388 loading_scenes: Default::default(),
389 }
390 }
391
392 fn request_with_options<P: AsRef<Path>>(&mut self, path: P, opts: SceneLoadingOptions) {
393 let path = path.as_ref().to_path_buf();
394
395 if self.loading_scenes.contains_key(&path) {
396 Log::warn(format!("A scene {} is already loading!", path.display()))
397 } else {
398 self.loading_scenes.insert(
400 path.clone(),
401 LoadingScene {
402 reported: false,
403 path: path.clone(),
404 options: opts,
405 },
406 );
407
408 let sender = self.sender.clone();
410 let serialization_context = self.serialization_context.clone();
411 let dyn_type_constructors = self.dyn_type_constructors.clone();
412 let resource_manager = self.resource_manager.clone();
413 let uuid = resource_manager.find::<Model>(&path).resource_uuid();
414
415 let io = resource_manager.resource_io();
417 resource_manager.task_pool().spawn_task(async move {
418 match SceneLoader::from_file(
419 path.clone(),
420 io.as_ref(),
421 serialization_context,
422 dyn_type_constructors,
423 resource_manager.clone(),
424 )
425 .await
426 {
427 Ok((loader, data)) => {
428 let scene = loader.finish().await;
429 Log::verify(sender.send(SceneLoadingResult {
430 uuid,
431 path,
432 result: Ok((scene, data)),
433 }));
434 }
435 Err(e) => {
436 Log::verify(sender.send(SceneLoadingResult {
437 uuid,
438 path,
439 result: Err(e),
440 }));
441 }
442 }
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(crate) enum GameErrorSource {
469 PluginMethod(&'static str),
470 ScriptMethod {
471 scene_handle: Handle<Scene>,
472 node_handle: Handle<Node>,
473 method_name: &'static str,
474 },
475}
476
477pub(crate) struct GameErrorContainer {
478 source: GameErrorSource,
479 error: GameError,
480}
481
482type ErrorQueue = VecDeque<GameErrorContainer>;
483
484pub struct Engine {
486 pub graphics_context: GraphicsContext,
488
489 pub resource_manager: ResourceManager,
494
495 pub user_interfaces: UiContainer,
497
498 pub scenes: SceneContainer,
500
501 pub async_scene_loader: AsyncSceneLoader,
503
504 pub task_pool: TaskPoolHandler,
506
507 performance_statistics: PerformanceStatistics,
508
509 model_events_receiver: Receiver<ResourceEvent>,
510
511 #[allow(dead_code)] sound_engine: SoundEngine,
513
514 plugins: Vec<PluginContainer>,
516
517 plugins_enabled: bool,
518
519 elapsed_time: f32,
521
522 input_state: InputState,
523
524 pub serialization_context: Arc<SerializationContext>,
527
528 pub widget_constructors: Arc<WidgetConstructorContainer>,
530
531 pub dyn_type_constructors: Arc<DynTypeConstructorContainer>,
534
535 pub script_processor: ScriptProcessor,
537
538 error_queue: ErrorQueue,
539}
540
541#[derive(Debug, Hash, PartialEq, Eq)]
542enum MessageTypeId {
543 Static(TypeId),
544 Dynamic(DynamicTypeId),
545}
546
547pub struct ScriptMessageDispatcher {
549 type_groups: FxHashMap<MessageTypeId, FxHashSet<Handle<Node>>>,
550 message_receiver: Receiver<ScriptMessage>,
551}
552
553fn try_enqueue_plugin_error(method_name: &'static str, result: GameResult, queue: &mut ErrorQueue) {
554 if let Err(error) = result {
555 queue.push_back(GameErrorContainer {
556 source: GameErrorSource::PluginMethod(method_name),
557 error,
558 });
559 }
560}
561
562impl ScriptMessageDispatcher {
563 fn new(message_receiver: Receiver<ScriptMessage>) -> Self {
564 Self {
565 type_groups: Default::default(),
566 message_receiver,
567 }
568 }
569
570 pub fn subscribe_to<T: 'static>(&mut self, receiver: Handle<Node>) {
573 self.subscribe_internal_to(receiver, MessageTypeId::Static(TypeId::of::<T>()));
574 }
575
576 pub fn subscribe_dynamic_to(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
579 self.subscribe_internal_to(receiver, MessageTypeId::Dynamic(type_id));
580 }
581
582 #[inline]
583 fn subscribe_internal_to(&mut self, receiver: Handle<Node>, type_id: MessageTypeId) {
584 self.type_groups
585 .entry(type_id)
586 .and_modify(|v| {
587 v.insert(receiver);
588 })
589 .or_insert_with(|| FxHashSet::from_iter([receiver]));
590 }
591
592 pub fn unsubscribe_from<T: 'static>(&mut self, receiver: Handle<Node>) {
594 self.unsubscribe_internal_from(receiver, &MessageTypeId::Static(TypeId::of::<T>()));
595 }
596
597 pub fn unsubscribe_dynamic_from(&mut self, receiver: Handle<Node>, type_id: DynamicTypeId) {
599 self.unsubscribe_internal_from(receiver, &MessageTypeId::Dynamic(type_id));
600 }
601
602 #[inline]
603 fn unsubscribe_internal_from(&mut self, receiver: Handle<Node>, type_id: &MessageTypeId) {
604 if let Some(group) = self.type_groups.get_mut(type_id) {
605 group.remove(&receiver);
606 }
607 }
608
609 pub fn unsubscribe(&mut self, receiver: Handle<Node>) {
611 for group in self.type_groups.values_mut() {
612 group.remove(&receiver);
613 }
614 }
615
616 fn dispatch_messages(
617 &self,
618 scene: &mut Scene,
619 scene_handle: Handle<Scene>,
620 plugins: &mut [PluginContainer],
621 resource_manager: &ResourceManager,
622 dt: f32,
623 elapsed_time: f32,
624 message_sender: &ScriptMessageSender,
625 user_interfaces: &mut UiContainer,
626 graphics_context: &mut GraphicsContext,
627 task_pool: &mut TaskPoolHandler,
628 input_state: &InputState,
629 error_queue: &mut ErrorQueue,
630 ) {
631 while let Ok(message) = self.message_receiver.try_recv() {
632 let type_id = match message.payload.get_dynamic_type_id() {
633 Some(it) => MessageTypeId::Dynamic(it),
634 None => MessageTypeId::Static(message.payload.deref().type_id()),
635 };
636 let receivers = self.type_groups.get(&type_id);
637
638 if receivers.is_none_or(|r| r.is_empty()) {
639 Log::warn(format!(
640 "Script message {message:?} was sent, but there's no receivers. \
641 Did you forgot to subscribe your script to the message?"
642 ));
643 }
644
645 if let Some(receivers) = receivers {
646 let mut payload = message.payload;
647
648 match message.kind {
649 ScriptMessageKind::Targeted(target) => {
650 if receivers.contains(&target) {
651 let mut context = ScriptMessageContext {
652 dt,
653 elapsed_time,
654 plugins: PluginsRefMut(plugins),
655 handle: target,
656 scene,
657 scene_handle,
658 resource_manager,
659 message_sender,
660 task_pool,
661 graphics_context,
662 user_interfaces,
663 script_index: 0,
664 input_state,
665 };
666
667 process_node_scripts(
668 "on_message",
669 &mut context,
670 scene_handle,
671 error_queue,
672 &mut |s, ctx| s.on_message(&mut *payload, ctx),
673 )
674 }
675 }
676 ScriptMessageKind::Hierarchical { root, routing } => match routing {
677 RoutingStrategy::Up => {
678 let mut node = root;
679 while let Ok(node_ref) = scene.graph.try_get_node(node) {
680 let parent = node_ref.parent();
681
682 let mut context = ScriptMessageContext {
683 dt,
684 elapsed_time,
685 plugins: PluginsRefMut(plugins),
686 handle: node,
687 scene,
688 scene_handle,
689 resource_manager,
690 message_sender,
691 task_pool,
692 graphics_context,
693 user_interfaces,
694 script_index: 0,
695 input_state,
696 };
697
698 if receivers.contains(&node) {
699 process_node_scripts(
700 "on_message",
701 &mut context,
702 scene_handle,
703 error_queue,
704 &mut |s, ctx| s.on_message(&mut *payload, ctx),
705 );
706 }
707
708 node = parent;
709 }
710 }
711 RoutingStrategy::Down => {
712 for node in scene.graph.traverse_handle_iter(root).collect::<Vec<_>>() {
713 let mut context = ScriptMessageContext {
714 dt,
715 elapsed_time,
716 plugins: PluginsRefMut(plugins),
717 handle: node,
718 scene,
719 scene_handle,
720 resource_manager,
721 message_sender,
722 task_pool,
723 graphics_context,
724 user_interfaces,
725 script_index: 0,
726 input_state,
727 };
728
729 if receivers.contains(&node) {
730 process_node_scripts(
731 "on_message",
732 &mut context,
733 scene_handle,
734 error_queue,
735 &mut |s, ctx| s.on_message(&mut *payload, ctx),
736 );
737 }
738 }
739 }
740 },
741 ScriptMessageKind::Global => {
742 for &node in receivers {
743 let mut context = ScriptMessageContext {
744 dt,
745 elapsed_time,
746 plugins: PluginsRefMut(plugins),
747 handle: node,
748 scene,
749 scene_handle,
750 resource_manager,
751 message_sender,
752 task_pool,
753 graphics_context,
754 user_interfaces,
755 script_index: 0,
756 input_state,
757 };
758
759 process_node_scripts(
760 "on_message",
761 &mut context,
762 scene_handle,
763 error_queue,
764 &mut |s, ctx| s.on_message(&mut *payload, ctx),
765 );
766 }
767 }
768 }
769 }
770 }
771 }
772}
773
774pub struct ScriptedScene {
776 pub handle: Handle<Scene>,
778 pub message_sender: ScriptMessageSender,
780 message_dispatcher: ScriptMessageDispatcher,
781}
782
783#[derive(Default)]
785pub struct ScriptProcessor {
786 wait_list: Vec<ResourceWaitContext>,
787 pub scripted_scenes: Vec<ScriptedScene>,
789}
790
791impl ScriptProcessor {
792 fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
793 self.scripted_scenes.iter().any(|s| s.handle == scene)
794 }
795
796 fn register_scripted_scene(
797 &mut self,
798 scene: Handle<Scene>,
799 resource_manager: &ResourceManager,
800 ) {
801 assert!(!self.has_scripted_scene(scene));
803
804 let (tx, rx) = channel();
805 self.scripted_scenes.push(ScriptedScene {
806 handle: scene,
807 message_sender: ScriptMessageSender { sender: tx },
808 message_dispatcher: ScriptMessageDispatcher::new(rx),
809 });
810
811 self.wait_list
812 .push(resource_manager.state().get_wait_context());
813 }
814
815 fn handle_scripts(
816 &mut self,
817 scenes: &mut SceneContainer,
818 plugins: &mut [PluginContainer],
819 resource_manager: &ResourceManager,
820 task_pool: &mut TaskPoolHandler,
821 graphics_context: &mut GraphicsContext,
822 user_interfaces: &mut UiContainer,
823 dt: f32,
824 elapsed_time: f32,
825 input_state: &InputState,
826 error_queue: &mut ErrorQueue,
827 ) {
828 self.wait_list
829 .retain_mut(|context| !context.is_all_loaded());
830
831 if !self.wait_list.is_empty() {
832 return;
833 }
834
835 self.scripted_scenes
836 .retain(|s| scenes.is_valid_handle(s.handle));
837
838 'scene_loop: for scripted_scene in self.scripted_scenes.iter_mut() {
839 let scene = &mut scenes[scripted_scene.handle];
840
841 if !*scene.enabled {
843 continue 'scene_loop;
844 }
845
846 let mut update_queue = VecDeque::new();
848 let mut start_queue = VecDeque::new();
849 let script_message_sender = scene.graph.script_message_sender.clone();
850 for (handle, node) in scene.graph.pair_iter_mut() {
851 node.scripts
853 .retain(|e| e.script.is_some() && !e.should_be_deleted);
854
855 if node.is_globally_enabled() {
856 for (i, entry) in node.scripts.iter().enumerate() {
857 if let Some(script) = entry.script.as_ref() {
858 if script.initialized {
859 if script.started {
860 update_queue.push_back((handle, i));
861 } else {
862 start_queue.push_back((handle, i));
863 }
864 } else {
865 script_message_sender
866 .send(NodeScriptMessage::InitializeScript {
867 handle,
868 script_index: i,
869 })
870 .unwrap();
871 }
872 }
873 }
874 }
875 }
876
877 let mut destruction_queue = VecDeque::new();
880
881 let max_iterations = 64;
882
883 'update_loop: for update_loop_iteration in 0..max_iterations {
884 let mut context = ScriptContext {
885 dt,
886 elapsed_time,
887 plugins: PluginsRefMut(plugins),
888 handle: Default::default(),
889 scene,
890 scene_handle: scripted_scene.handle,
891 resource_manager,
892 message_sender: &scripted_scene.message_sender,
893 message_dispatcher: &mut scripted_scene.message_dispatcher,
894 task_pool,
895 graphics_context,
896 user_interfaces,
897 script_index: 0,
898 input_state,
899 };
900
901 'init_loop: for init_loop_iteration in 0..max_iterations {
902 while let Ok(event) = context.scene.graph.script_message_receiver.try_recv() {
905 match event {
906 NodeScriptMessage::InitializeScript {
907 handle,
908 script_index,
909 } => {
910 context.handle = handle;
911 context.script_index = script_index;
912
913 process_node_script(
914 "on_init",
915 script_index,
916 scripted_scene.handle,
917 &mut context,
918 error_queue,
919 &mut |script, context| {
920 if !script.initialized {
921 script.on_init(context)?;
922 script.initialized = true;
923 }
924
925 start_queue.push_back((handle, script_index));
927
928 Ok(())
929 },
930 );
931 }
932 NodeScriptMessage::DestroyScript {
933 handle,
934 script,
935 script_index,
936 } => {
937 destruction_queue.push_back((handle, script, script_index));
939 }
940 }
941 }
942
943 if start_queue.is_empty() {
944 break 'init_loop;
946 } else {
947 while let Some((handle, script_index)) = start_queue.pop_front() {
951 context.handle = handle;
952 context.script_index = script_index;
953
954 process_node_script(
955 "on_start",
956 script_index,
957 scripted_scene.handle,
958 &mut context,
959 error_queue,
960 &mut |script, context| {
961 if script.initialized && !script.started {
962 let result = script.on_start(context);
963 script.started = true;
964 update_queue.push_back((handle, script_index));
965 result
966 } else {
967 Ok(())
968 }
969 },
970 );
971 }
972 }
973
974 if init_loop_iteration == max_iterations - 1 {
975 Log::warn(
976 "Infinite init loop detected! Most likely some of \
977 your scripts causing infinite prefab instantiation!",
978 )
979 }
980 }
981
982 if update_queue.is_empty() {
984 break 'update_loop;
985 } else {
986 while let Some((handle, script_index)) = update_queue.pop_front() {
987 context.handle = handle;
988 context.script_index = script_index;
989
990 process_node_script(
991 "on_update",
992 script_index,
993 scripted_scene.handle,
994 &mut context,
995 error_queue,
996 &mut |script, context| script.on_update(context),
997 );
998 }
999 }
1000
1001 if update_loop_iteration == max_iterations - 1 {
1002 Log::warn(
1003 "Infinite update loop detected! Most likely some of \
1004 your scripts causing infinite prefab instantiation!",
1005 )
1006 }
1007 }
1008
1009 scripted_scene.message_dispatcher.dispatch_messages(
1014 scene,
1015 scripted_scene.handle,
1016 plugins,
1017 resource_manager,
1018 dt,
1019 elapsed_time,
1020 &scripted_scene.message_sender,
1021 user_interfaces,
1022 graphics_context,
1023 task_pool,
1024 input_state,
1025 error_queue,
1026 );
1027
1028 let mut context = ScriptDeinitContext {
1030 elapsed_time,
1031 plugins: PluginsRefMut(plugins),
1032 resource_manager,
1033 scene,
1034 scene_handle: scripted_scene.handle,
1035 node_handle: Default::default(),
1036 message_sender: &scripted_scene.message_sender,
1037 user_interfaces,
1038 graphics_context,
1039 task_pool,
1040 script_index: 0,
1041 input_state,
1042 };
1043 while let Some((handle, mut script, index)) = destruction_queue.pop_front() {
1044 context.node_handle = handle;
1045 context.script_index = index;
1046
1047 scripted_scene.message_dispatcher.unsubscribe(handle);
1049
1050 if let Err(error) = script.on_deinit(&mut context) {
1053 error_queue.push_back(GameErrorContainer {
1054 source: GameErrorSource::ScriptMethod {
1055 scene_handle: scripted_scene.handle,
1056 node_handle: handle,
1057 method_name: "on_deinit",
1058 },
1059 error,
1060 });
1061 }
1062 }
1063 }
1064
1065 for (handle, mut detached_scene) in scenes.destruction_list.drain(..) {
1067 if let Some(scripted_scene) = self.scripted_scenes.iter().find(|s| s.handle == handle) {
1068 let mut context = ScriptDeinitContext {
1069 elapsed_time,
1070 plugins: PluginsRefMut(plugins),
1071 resource_manager,
1072 scene: &mut detached_scene,
1073 scene_handle: scripted_scene.handle,
1074 node_handle: Default::default(),
1075 message_sender: &scripted_scene.message_sender,
1076 task_pool,
1077 graphics_context,
1078 user_interfaces,
1079 script_index: 0,
1080 input_state,
1081 };
1082
1083 for node_index in 0..context.scene.graph.capacity() {
1085 let handle_node = context.scene.graph.handle_from_index(node_index);
1086 context.node_handle = handle_node;
1087
1088 process_node_scripts(
1089 "on_deinit",
1090 &mut context,
1091 scripted_scene.handle,
1092 error_queue,
1093 &mut |script, context| {
1094 if script.initialized {
1095 script.on_deinit(context)
1096 } else {
1097 Ok(())
1098 }
1099 },
1100 );
1101 }
1102 }
1103 }
1104 }
1105}
1106
1107struct ResourceGraphVertex {
1108 resource: ModelResource,
1109 children: Vec<ResourceGraphVertex>,
1110}
1111
1112impl ResourceGraphVertex {
1113 pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1114 let mut children = Vec::new();
1115
1116 let mut dependent_resources = HashSet::new();
1118 for resource in resource_manager.state().iter() {
1119 if let Some(other_model) = resource.try_cast::<Model>() {
1120 let mut state = other_model.state();
1121 if let Some(model_data) = state.data() {
1122 if model_data
1123 .get_scene()
1124 .graph
1125 .linear_iter()
1126 .any(|n| n.resource.as_ref() == Some(&model))
1127 {
1128 dependent_resources.insert(other_model.clone());
1129 }
1130 }
1131 }
1132 }
1133
1134 children.extend(
1135 dependent_resources
1136 .into_iter()
1137 .map(|r| ResourceGraphVertex::new(r, resource_manager.clone())),
1138 );
1139
1140 Self {
1141 resource: model,
1142 children,
1143 }
1144 }
1145
1146 pub fn resolve(&self) {
1147 Log::info(format!(
1148 "Resolving {} resource from dependency graph...",
1149 self.resource.resource_uuid()
1150 ));
1151
1152 if block_on(self.resource.clone()).is_ok() {
1154 self.resource.data_ref().get_scene_mut();
1155
1156 for child in self.children.iter() {
1157 child.resolve();
1158 }
1159 }
1160 }
1161}
1162
1163struct ResourceDependencyGraph {
1164 root: ResourceGraphVertex,
1165}
1166
1167impl ResourceDependencyGraph {
1168 pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self {
1169 Self {
1170 root: ResourceGraphVertex::new(model, resource_manager),
1171 }
1172 }
1173
1174 pub fn resolve(&self) {
1175 self.root.resolve()
1176 }
1177}
1178
1179pub type GraphicsServerConstructorResult = Result<(Window, SharedGraphicsServer), FrameworkError>;
1181
1182pub type GraphicsServerConstructorCallback = dyn Fn(
1187 &GraphicsContextParams,
1188 &ActiveEventLoop,
1189 WindowAttributes,
1190 bool,
1191) -> GraphicsServerConstructorResult;
1192
1193#[derive(Clone)]
1196pub struct GraphicsServerConstructor(Rc<GraphicsServerConstructorCallback>);
1197
1198impl Default for GraphicsServerConstructor {
1199 fn default() -> Self {
1200 Self(Rc::new(
1201 |params, window_target, window_builder, named_objects| {
1202 GlGraphicsServer::new(
1203 params.vsync,
1204 params.msaa_sample_count,
1205 window_target,
1206 window_builder,
1207 named_objects,
1208 )
1209 },
1210 ))
1211 }
1212}
1213
1214#[derive(Clone)]
1216pub struct GraphicsContextParams {
1217 pub window_attributes: WindowAttributes,
1219
1220 pub vsync: bool,
1223
1224 pub msaa_sample_count: Option<u8>,
1227
1228 pub graphics_server_constructor: GraphicsServerConstructor,
1230
1231 pub named_objects: bool,
1235}
1236
1237impl Default for GraphicsContextParams {
1238 fn default() -> Self {
1239 Self {
1240 window_attributes: Default::default(),
1241 vsync: true,
1242 msaa_sample_count: None,
1243 graphics_server_constructor: Default::default(),
1244 named_objects: false,
1245 }
1246 }
1247}
1248
1249pub struct EngineInitParams {
1251 pub graphics_context_params: GraphicsContextParams,
1256 pub serialization_context: Arc<SerializationContext>,
1258 pub widget_constructors: Arc<WidgetConstructorContainer>,
1260 pub dyn_type_constructors: Arc<DynTypeConstructorContainer>,
1262 pub resource_manager: ResourceManager,
1264 pub task_pool: Arc<TaskPool>,
1266}
1267
1268fn process_node_script<T, C>(
1269 caller_name: &'static str,
1270 index: usize,
1271 scene_handle: Handle<Scene>,
1272 context: &mut C,
1273 error_queue: &mut ErrorQueue,
1274 func: &mut T,
1275) -> bool
1276where
1277 T: FnMut(&mut Script, &mut C) -> GameResult,
1278 C: UniversalScriptContext,
1279{
1280 let Ok(node) = context.node() else {
1281 return false;
1283 };
1284
1285 if !node.is_globally_enabled() {
1286 return false;
1287 }
1288
1289 let Some(entry) = node.scripts.get_mut(index) else {
1290 return false;
1292 };
1293
1294 let Some(mut script) = entry.take() else {
1295 return false;
1296 };
1297
1298 if let Err(error) = func(&mut script, context) {
1299 let node = context.node();
1300 let handle = node.map_or(Handle::NONE, |n| n.handle());
1301 error_queue.push_back(GameErrorContainer {
1302 source: GameErrorSource::ScriptMethod {
1303 scene_handle,
1304 node_handle: handle,
1305 method_name: caller_name,
1306 },
1307 error,
1308 });
1309 }
1310
1311 match context.node() {
1312 Ok(node) => {
1313 let entry = node
1314 .scripts
1315 .get_mut(index)
1316 .expect("Scripts array cannot be modified!");
1317
1318 if entry.should_be_deleted {
1319 context.destroy_script_deferred(script, index);
1320 } else {
1321 entry.script = Some(script);
1323 }
1324 }
1325 Err(_) => {
1326 context.destroy_script_deferred(script, index);
1329 }
1330 }
1331
1332 true
1333}
1334
1335fn process_node_scripts<T, C>(
1336 caller_name: &'static str,
1337 context: &mut C,
1338 scene_handle: Handle<Scene>,
1339 error_queue: &mut ErrorQueue,
1340 func: &mut T,
1341) where
1342 T: FnMut(&mut Script, &mut C) -> GameResult,
1343 C: UniversalScriptContext,
1344{
1345 let mut index = 0;
1346 loop {
1347 context.set_script_index(index);
1348
1349 if !process_node_script(caller_name, index, scene_handle, context, error_queue, func) {
1350 return;
1351 }
1352
1353 index += 1;
1355 }
1356}
1357
1358pub(crate) fn process_scripts<T>(
1359 caller_name: &'static str,
1360 scene: &mut Scene,
1361 scene_handle: Handle<Scene>,
1362 plugins: &mut [PluginContainer],
1363 resource_manager: &ResourceManager,
1364 message_sender: &ScriptMessageSender,
1365 message_dispatcher: &mut ScriptMessageDispatcher,
1366 task_pool: &mut TaskPoolHandler,
1367 graphics_context: &mut GraphicsContext,
1368 user_interfaces: &mut UiContainer,
1369 dt: f32,
1370 elapsed_time: f32,
1371 input_state: &InputState,
1372 error_queue: &mut ErrorQueue,
1373 mut func: T,
1374) where
1375 T: FnMut(&mut Script, &mut ScriptContext) -> GameResult,
1376{
1377 let mut context = ScriptContext {
1378 dt,
1379 elapsed_time,
1380 plugins: PluginsRefMut(plugins),
1381 handle: Default::default(),
1382 scene,
1383 scene_handle,
1384 resource_manager,
1385 message_sender,
1386 message_dispatcher,
1387 task_pool,
1388 graphics_context,
1389 user_interfaces,
1390 script_index: 0,
1391 input_state,
1392 };
1393
1394 for node_index in 0..context.scene.graph.capacity() {
1395 context.handle = context.scene.graph.handle_from_index(node_index);
1396
1397 process_node_scripts(
1398 caller_name,
1399 &mut context,
1400 scene_handle,
1401 error_queue,
1402 &mut func,
1403 );
1404 }
1405}
1406
1407pub(crate) fn initialize_resource_manager_loaders(
1408 resource_manager: &ResourceManager,
1409 serialization_context: Arc<SerializationContext>,
1410 widget_constructors: Arc<WidgetConstructorContainer>,
1411 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
1412) {
1413 let model_loader = ModelLoader {
1414 resource_manager: resource_manager.clone(),
1415 serialization_context,
1416 dyn_type_constructors: dyn_type_constructors.clone(),
1417 default_import_options: Default::default(),
1418 };
1419
1420 let mut state = resource_manager.state();
1421
1422 for shader in ShaderResource::standard_shaders() {
1423 state.built_in_resources.add((*shader).clone());
1424 }
1425 state.built_in_resources.add(GLTF_SHADER.clone());
1426
1427 for texture in SkyBoxKind::built_in_skybox_textures() {
1428 state.built_in_resources.add(texture.clone());
1429 }
1430
1431 state.built_in_resources.add(BUILT_IN_FONT.clone());
1432 state.built_in_resources.add(BUILT_IN_BOLD.clone());
1433 state.built_in_resources.add(BUILT_IN_ITALIC.clone());
1434 state.built_in_resources.add(BOLD_ITALIC.clone());
1435
1436 state.built_in_resources.add(texture::PLACEHOLDER.clone());
1437 state.built_in_resources.add(texture::PURE_COLOR.clone());
1438 state.built_in_resources.add(style::DEFAULT_STYLE.clone());
1439
1440 for material in [
1441 &*material::STANDARD,
1442 &*material::STANDARD_2D,
1443 &*material::STANDARD_SPRITE,
1444 &*material::STANDARD_TERRAIN,
1445 &*material::STANDARD_TWOSIDES,
1446 &*material::STANDARD_PARTICLE_SYSTEM,
1447 &*material::STANDARD_WIDGET,
1448 ] {
1449 state.built_in_resources.add(material.clone());
1450 }
1451
1452 for surface in [
1453 &*surface::CUBE,
1454 &*surface::QUAD,
1455 &*surface::CYLINDER,
1456 &*surface::SPHERE,
1457 &*surface::CONE,
1458 &*surface::TORUS,
1459 ] {
1460 state.built_in_resources.add(surface.clone());
1461 }
1462
1463 state.constructors_container.add::<Texture>();
1464 state.constructors_container.add::<Shader>();
1465 state.constructors_container.add::<Model>();
1466 state.constructors_container.add::<CurveResourceState>();
1467 state.constructors_container.add::<SoundBuffer>();
1468 state.constructors_container.add::<HrirSphereResourceData>();
1469 state.constructors_container.add::<Material>();
1470 state.constructors_container.add::<Font>();
1471 state.constructors_container.add::<UserInterface>();
1472 state.constructors_container.add::<SurfaceData>();
1473 state.constructors_container.add::<TileSet>();
1474 state.constructors_container.add::<TileMapBrush>();
1475 state.constructors_container.add::<TileMapData>();
1476 state.constructors_container.add::<CustomTileCollider>();
1477 state.constructors_container.add::<AnimationTracksData>();
1478 state.constructors_container.add::<Style>();
1479
1480 let mut loaders = state.loaders.safe_lock();
1481 let gltf_loader = super::resource::gltf::GltfLoader {
1482 resource_manager: resource_manager.clone(),
1483 default_import_options: Default::default(),
1484 };
1485 loaders.set(gltf_loader);
1486 loaders.set(model_loader);
1487 loaders.set(TextureLoader {
1488 default_import_options: Default::default(),
1489 });
1490 loaders.set(SoundBufferLoader {
1491 default_import_options: Default::default(),
1492 });
1493 loaders.set(ShaderLoader);
1494 loaders.set(CurveLoader);
1495 loaders.set(HrirSphereLoader);
1496 loaders.set(MaterialLoader {
1497 resource_manager: resource_manager.clone(),
1498 });
1499 loaders.set(FontLoader::new(resource_manager.clone()));
1500 loaders.set(UserInterfaceLoader {
1501 resource_manager: resource_manager.clone(),
1502 constructors: widget_constructors,
1503 dyn_type_constructors,
1504 });
1505 loaders.set(SurfaceDataLoader {});
1506 loaders.set(TileSetLoader {
1507 resource_manager: resource_manager.clone(),
1508 });
1509 loaders.set(TileMapBrushLoader {
1510 resource_manager: resource_manager.clone(),
1511 });
1512 loaders.set(StyleLoader {
1513 resource_manager: resource_manager.clone(),
1514 });
1515}
1516
1517#[derive(Copy, Clone)]
1519pub enum ApplicationLoopController<'a> {
1520 Headless {
1523 running: &'a Cell<bool>,
1526 },
1527 ActiveEventLoop(&'a ActiveEventLoop),
1529 EventLoop(&'a EventLoop<()>),
1531}
1532
1533impl ApplicationLoopController<'_> {
1534 pub fn exit(&self) {
1536 match self {
1537 ApplicationLoopController::Headless { running } => running.set(false),
1538 ApplicationLoopController::ActiveEventLoop(event_loop) => event_loop.exit(),
1539 ApplicationLoopController::EventLoop(_) => {
1540 warn!("Can't exit the loop until it is activated!")
1541 }
1542 }
1543 }
1544
1545 pub fn exiting(&self) -> bool {
1548 match self {
1549 ApplicationLoopController::Headless { running } => !running.get(),
1550 ApplicationLoopController::ActiveEventLoop(event_loop) => event_loop.exiting(),
1551 ApplicationLoopController::EventLoop(_) => false,
1552 }
1553 }
1554}
1555
1556impl Engine {
1557 #[inline]
1603 #[allow(unused_variables)]
1604 pub fn new(params: EngineInitParams) -> Result<Self, EngineError> {
1605 let EngineInitParams {
1606 graphics_context_params,
1607 serialization_context,
1608 widget_constructors,
1609 dyn_type_constructors,
1610 resource_manager,
1611 task_pool,
1612 } = params;
1613
1614 #[cfg(target_arch = "wasm32")]
1615 wasm_utils::set_panic_hook();
1616
1617 initialize_resource_manager_loaders(
1618 &resource_manager,
1619 serialization_context.clone(),
1620 widget_constructors.clone(),
1621 dyn_type_constructors.clone(),
1622 );
1623
1624 let (rx, tx) = channel();
1625 resource_manager.state().event_broadcaster.add(rx);
1626
1627 let sound_engine = SoundEngine::without_device();
1628
1629 let user_interfaces =
1630 UiContainer::new_with_ui(UserInterface::new(Vector2::new(100.0, 100.0)));
1631
1632 Ok(Self {
1633 graphics_context: GraphicsContext::Uninitialized(graphics_context_params),
1634 model_events_receiver: tx,
1635 async_scene_loader: AsyncSceneLoader::new(
1636 resource_manager.clone(),
1637 serialization_context.clone(),
1638 dyn_type_constructors.clone(),
1639 ),
1640 resource_manager,
1641 scenes: SceneContainer::new(sound_engine.clone()),
1642 sound_engine,
1643 user_interfaces,
1644 performance_statistics: Default::default(),
1645 plugins: Default::default(),
1646 serialization_context,
1647 widget_constructors,
1648 dyn_type_constructors,
1649 script_processor: Default::default(),
1650 plugins_enabled: false,
1651 elapsed_time: 0.0,
1652 task_pool: TaskPoolHandler::new(task_pool),
1653 input_state: Default::default(),
1654 error_queue: Default::default(),
1655 })
1656 }
1657
1658 pub fn initialize_graphics_context(
1666 &mut self,
1667 event_loop: &ActiveEventLoop,
1668 ) -> Result<(), EngineError> {
1669 if let GraphicsContext::Uninitialized(params) = &self.graphics_context {
1670 let (window, server) = params.graphics_server_constructor.0(
1671 params,
1672 event_loop,
1673 params.window_attributes.clone(),
1674 params.named_objects,
1675 )?;
1676 let frame_size = (window.inner_size().width, window.inner_size().height);
1677
1678 let renderer = Renderer::new(server, frame_size, &self.resource_manager)?;
1679
1680 for ui in self.user_interfaces.iter_mut() {
1681 ui.set_screen_size(Vector2::new(frame_size.0 as f32, frame_size.1 as f32));
1682 }
1683
1684 self.graphics_context = GraphicsContext::Initialized(InitializedGraphicsContext {
1685 renderer,
1686 window,
1687 params: params.clone(),
1688 });
1689
1690 if let Err(err) = self.sound_engine.initialize_audio_output_device() {
1691 Log::err(format!(
1692 "Unable to initialize audio output device! Reason: {err:?}"
1693 ));
1694 }
1695
1696 Ok(())
1697 } else {
1698 Err(EngineError::Custom(
1699 "Graphics context is already initialized!".to_string(),
1700 ))
1701 }
1702 }
1703
1704 pub fn destroy_graphics_context(&mut self) -> Result<(), EngineError> {
1711 if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
1712 let params = &ctx.params;
1713 let window = &ctx.window;
1714
1715 let mut window_attributes = WindowAttributes::default();
1716
1717 window_attributes.inner_size = Some(Size::Physical(window.inner_size()));
1718 window_attributes.min_inner_size = params.window_attributes.min_inner_size;
1719 window_attributes.max_inner_size = params.window_attributes.max_inner_size;
1720 window_attributes.position = window.outer_position().ok().map(Position::Physical);
1721 window_attributes.resizable = window.is_resizable();
1722 window_attributes.enabled_buttons = window.enabled_buttons();
1723 window_attributes.title = window.title();
1724 window_attributes.maximized = window.is_maximized();
1725 window_attributes.visible = window.is_visible().unwrap_or(true);
1726 window_attributes.transparent = params.window_attributes.transparent;
1727 window_attributes.decorations = window.is_decorated();
1728 window_attributes.preferred_theme = params.window_attributes.preferred_theme;
1729 window_attributes.resize_increments = window.resize_increments().map(Size::Physical);
1730 window_attributes.content_protected = params.window_attributes.content_protected;
1731 window_attributes.window_level = params.window_attributes.window_level;
1732 window_attributes.active = params.window_attributes.active;
1733 window_attributes
1734 .window_icon
1735 .clone_from(¶ms.window_attributes.window_icon);
1736
1737 self.graphics_context = GraphicsContext::Uninitialized(GraphicsContextParams {
1738 window_attributes,
1739 vsync: params.vsync,
1740 msaa_sample_count: params.msaa_sample_count,
1741 graphics_server_constructor: params.graphics_server_constructor.clone(),
1742 named_objects: params.named_objects,
1743 });
1744
1745 self.sound_engine.destroy_audio_output_device();
1746
1747 Ok(())
1748 } else {
1749 Err(EngineError::Custom(
1750 "Graphics context is already destroyed!".to_string(),
1751 ))
1752 }
1753 }
1754
1755 pub fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
1758 if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
1759 ctx.renderer.set_frame_size(new_size)?;
1760 }
1761
1762 Ok(())
1763 }
1764
1765 pub fn elapsed_time(&self) -> f32 {
1769 self.elapsed_time
1770 }
1771
1772 pub fn update(
1786 &mut self,
1787 dt: f32,
1788 controller: ApplicationLoopController,
1789 lag: &mut f32,
1790 switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
1791 ) {
1792 self.handle_async_scene_loading(dt, lag, controller);
1793 self.pre_update(dt, controller, lag, switches);
1794 self.post_update(dt, &Default::default(), lag, controller);
1795 self.handle_plugins_hot_reloading(dt, controller, lag, |_| {});
1796 }
1797
1798 pub fn handle_plugins_hot_reloading<F>(
1806 &mut self,
1807 #[allow(unused_variables)] dt: f32,
1808 #[allow(unused_variables)] controller: ApplicationLoopController,
1809 #[allow(unused_variables)] lag: &mut f32,
1810 #[allow(unused_variables)] on_reloaded: F,
1811 ) where
1812 F: FnMut(&dyn Plugin),
1813 {
1814 #[cfg(any(unix, windows))]
1815 {
1816 if let Err(message) = self.reload_dynamic_plugins(dt, controller, lag, on_reloaded) {
1817 Log::err(format!(
1818 "Unable to reload dynamic plugins. Reason: {message}"
1819 ))
1820 }
1821 }
1822 }
1823
1824 fn handle_async_scene_loading(
1825 &mut self,
1826 dt: f32,
1827 lag: &mut f32,
1828 controller: ApplicationLoopController,
1829 ) {
1830 let len = self.async_scene_loader.loading_scenes.len();
1831 let mut n = 0;
1832 while n < len {
1833 if let Some(request) = self.async_scene_loader.loading_scenes.values_mut().nth(n) {
1834 if !request.reported {
1835 request.reported = true;
1836
1837 if self.plugins_enabled {
1839 let path = request.path.clone();
1840 let mut context = PluginContext {
1841 scenes: &mut self.scenes,
1842 resource_manager: &self.resource_manager,
1843 graphics_context: &mut self.graphics_context,
1844 dt,
1845 lag,
1846 user_interfaces: &mut self.user_interfaces,
1847 serialization_context: &self.serialization_context,
1848 widget_constructors: &self.widget_constructors,
1849 dyn_type_constructors: &self.dyn_type_constructors,
1850 performance_statistics: &self.performance_statistics,
1851 elapsed_time: self.elapsed_time,
1852 script_processor: &self.script_processor,
1853 async_scene_loader: &mut self.async_scene_loader,
1854 loop_controller: controller,
1855 task_pool: &mut self.task_pool,
1856 input_state: &self.input_state,
1857 };
1858
1859 for plugin in self.plugins.iter_mut() {
1860 try_enqueue_plugin_error(
1861 "on_scene_begin_loading",
1862 plugin.on_scene_begin_loading(&path, &mut context),
1863 &mut self.error_queue,
1864 );
1865 }
1866 }
1867 }
1868 }
1869
1870 n += 1;
1871 }
1872
1873 while let Ok(loading_result) = self.async_scene_loader.receiver.try_recv() {
1874 if let Some(request) = self
1875 .async_scene_loader
1876 .loading_scenes
1877 .remove(&loading_result.path)
1878 {
1879 let mut context = PluginContext {
1880 scenes: &mut self.scenes,
1881 resource_manager: &self.resource_manager,
1882 graphics_context: &mut self.graphics_context,
1883 dt,
1884 lag,
1885 user_interfaces: &mut self.user_interfaces,
1886 serialization_context: &self.serialization_context,
1887 widget_constructors: &self.widget_constructors,
1888 dyn_type_constructors: &self.dyn_type_constructors,
1889 performance_statistics: &self.performance_statistics,
1890 elapsed_time: self.elapsed_time,
1891 script_processor: &self.script_processor,
1892 async_scene_loader: &mut self.async_scene_loader,
1893 loop_controller: controller,
1894 task_pool: &mut self.task_pool,
1895 input_state: &self.input_state,
1896 };
1897
1898 match loading_result.result {
1899 Ok((mut scene, data)) => {
1900 if request.options.derived {
1901 let model = self
1902 .resource_manager
1903 .find_uuid::<Model>(loading_result.uuid);
1904 let data = Model {
1907 mapping: NodeMapping::UseHandles,
1908 scene: scene
1912 .clone_ex(
1913 scene.graph.get_root(),
1914 true,
1915 &mut |_, _| true,
1916 &mut |_, _| {},
1917 &mut |_, _, _| {},
1918 )
1919 .0,
1920 };
1921 model.header().state.commit_ok(data);
1922
1923 for (handle, node) in scene.graph.pair_iter_mut() {
1924 node.set_inheritance_data(handle, model.clone());
1925 }
1926
1927 (&mut scene as &mut dyn Reflect).apply_recursively_mut(
1930 &mut |object| {
1931 let type_id = (*object).type_id();
1932 if type_id != TypeId::of::<NodePool>() {
1933 object.as_inheritable_variable_mut(&mut |variable| {
1934 if let Some(variable) = variable {
1935 variable.reset_modified_flag();
1936 }
1937 });
1938 }
1939 },
1940 &[
1941 TypeId::of::<UntypedResource>(),
1942 TypeId::of::<navmesh::Container>(),
1943 ],
1944 )
1945 } else {
1946 if let Some(source_asset) =
1948 scene.graph[scene.graph.get_root()].root_resource()
1949 {
1950 let source_asset_ref = source_asset.data_ref();
1951 let source_scene_ref = &source_asset_ref.scene;
1952 Log::verify(try_inherit_properties(
1953 &mut scene,
1954 source_scene_ref,
1955 &[
1956 TypeId::of::<NodePool>(),
1957 TypeId::of::<UntypedResource>(),
1958 TypeId::of::<navmesh::Container>(),
1959 ],
1960 ));
1961 }
1962 }
1963
1964 let scene_handle = context.scenes.add(scene);
1965
1966 if self.plugins_enabled {
1968 for plugin in self.plugins.iter_mut() {
1969 Log::info(format!(
1970 "Scene {} was loaded successfully!",
1971 loading_result.path.display()
1972 ));
1973
1974 try_enqueue_plugin_error(
1975 "on_scene_loaded",
1976 plugin.on_scene_loaded(
1977 &request.path,
1978 scene_handle,
1979 &data,
1980 &mut context,
1981 ),
1982 &mut self.error_queue,
1983 );
1984 }
1985 }
1986 }
1987 Err(error) => {
1988 if self.plugins_enabled {
1990 Log::err(format!(
1991 "Unable to load scene {:?}. Reason: {}",
1992 loading_result.path, error
1993 ));
1994
1995 for plugin in self.plugins.iter_mut() {
1996 try_enqueue_plugin_error(
1997 "on_scene_loading_failed",
1998 plugin.on_scene_loading_failed(
1999 &request.path,
2000 &error,
2001 &mut context,
2002 ),
2003 &mut self.error_queue,
2004 )
2005 }
2006 }
2007 }
2008 }
2009 }
2010 }
2011 }
2012
2013 pub fn pre_update(
2028 &mut self,
2029 dt: f32,
2030 controller: ApplicationLoopController,
2031 lag: &mut f32,
2032 switches: FxHashMap<Handle<Scene>, GraphUpdateSwitches>,
2033 ) {
2034 self.update_plugins(dt, controller, lag);
2037 self.handle_scripts(dt);
2038
2039 self.resource_manager.state().update(dt);
2043 self.handle_model_events();
2044
2045 let window_size = if let GraphicsContext::Initialized(ctx) = &mut self.graphics_context {
2046 let inner_size = ctx.window.inner_size();
2047 let window_size = Vector2::new(inner_size.width as f32, inner_size.height as f32);
2048 ctx.renderer.update_caches(&self.resource_manager, dt);
2049 window_size
2050 } else {
2051 Vector2::new(1.0, 1.0)
2052 };
2053
2054 for (handle, scene) in self.scenes.pair_iter_mut().filter(|(_, s)| *s.enabled) {
2055 let frame_size =
2056 scene
2057 .rendering_options
2058 .render_target
2059 .as_ref()
2060 .map_or(window_size, |rt| {
2061 if let TextureKind::Rectangle { width, height } = rt.data_ref().kind() {
2062 Vector2::new(width as f32, height as f32)
2063 } else {
2064 panic!("only rectangle textures can be used as render target!");
2065 }
2066 });
2067
2068 scene.update(
2069 frame_size,
2070 dt,
2071 switches.get(&handle).cloned().unwrap_or_default(),
2072 );
2073 }
2074 }
2075
2076 pub fn post_update(
2081 &mut self,
2082 dt: f32,
2083 ui_update_switches: &UiUpdateSwitches,
2084 lag: &mut f32,
2085 controller: ApplicationLoopController,
2086 ) {
2087 let screen_size = if let GraphicsContext::Initialized(ref ctx) = self.graphics_context {
2088 let inner_size = ctx.window.inner_size();
2089 Some(Vector2::new(
2090 inner_size.width as f32,
2091 inner_size.height as f32,
2092 ))
2093 } else {
2094 None
2095 };
2096
2097 let time = instant::Instant::now();
2098 for ui in self.user_interfaces.iter_mut() {
2099 let screen_size = screen_size.unwrap_or_else(|| ui.screen_size());
2100 ui.update(screen_size, dt, ui_update_switches);
2101 }
2102 self.performance_statistics.ui_time = instant::Instant::now() - time;
2103 self.elapsed_time += dt;
2104
2105 if let GraphicsContext::Initialized(_) = self.graphics_context {
2106 self.post_update_plugins(dt, controller, lag);
2107
2108 self.input_state.mouse.speed = Vector2::default();
2109 self.input_state.keyboard.released_keys.clear();
2110 self.input_state.keyboard.pressed_keys.clear();
2111 }
2112 }
2113
2114 pub fn has_scripted_scene(&self, scene: Handle<Scene>) -> bool {
2116 self.script_processor.has_scripted_scene(scene)
2117 }
2118
2119 pub fn register_scripted_scene(&mut self, scene: Handle<Scene>) {
2121 self.script_processor
2122 .register_scripted_scene(scene, &self.resource_manager)
2123 }
2124
2125 fn handle_scripts(&mut self, dt: f32) {
2126 let time = instant::Instant::now();
2127
2128 self.script_processor.handle_scripts(
2129 &mut self.scenes,
2130 &mut self.plugins,
2131 &self.resource_manager,
2132 &mut self.task_pool,
2133 &mut self.graphics_context,
2134 &mut self.user_interfaces,
2135 dt,
2136 self.elapsed_time,
2137 &self.input_state,
2138 &mut self.error_queue,
2139 );
2140
2141 self.performance_statistics.scripts_time = instant::Instant::now() - time;
2142 }
2143
2144 fn handle_async_tasks(
2145 &mut self,
2146 dt: f32,
2147 controller: ApplicationLoopController,
2148 lag: &mut f32,
2149 ) {
2150 while let Some(result) = self.task_pool.inner().next_task_result() {
2151 if let Some(plugin_task_handler) = self.task_pool.pop_plugin_task_handler(result.id) {
2152 let mut ctx = PluginContext {
2154 scenes: &mut self.scenes,
2155 resource_manager: &self.resource_manager,
2156 graphics_context: &mut self.graphics_context,
2157 dt,
2158 lag,
2159 user_interfaces: &mut self.user_interfaces,
2160 serialization_context: &self.serialization_context,
2161 widget_constructors: &self.widget_constructors,
2162 dyn_type_constructors: &self.dyn_type_constructors,
2163 performance_statistics: &self.performance_statistics,
2164 elapsed_time: self.elapsed_time,
2165 script_processor: &self.script_processor,
2166 async_scene_loader: &mut self.async_scene_loader,
2167 loop_controller: controller,
2168 task_pool: &mut self.task_pool,
2169 input_state: &self.input_state,
2170 };
2171 try_enqueue_plugin_error(
2172 "handle_async_tasks",
2173 (plugin_task_handler)(result.payload, &mut self.plugins, &mut ctx),
2174 &mut self.error_queue,
2175 )
2176 } else if let Some(node_task_handler) = self.task_pool.pop_node_task_handler(result.id)
2177 {
2178 if let Some(scripted_scene) = self
2180 .script_processor
2181 .scripted_scenes
2182 .iter_mut()
2183 .find(|e| e.handle == node_task_handler.scene_handle)
2184 {
2185 let payload = result.payload;
2186 if let Ok(scene) = self.scenes.try_get_mut(node_task_handler.scene_handle) {
2187 if let Ok(node) =
2188 scene.graph.try_get_node_mut(node_task_handler.node_handle)
2189 {
2190 if let Some(mut script) = node
2191 .scripts
2192 .get_mut(node_task_handler.script_index)
2193 .and_then(|e| e.script.take())
2194 {
2195 let mut ctx = ScriptContext {
2196 dt,
2197 elapsed_time: self.elapsed_time,
2198 plugins: PluginsRefMut(&mut self.plugins),
2199 handle: node_task_handler.node_handle,
2200 scene,
2201 scene_handle: scripted_scene.handle,
2202 resource_manager: &self.resource_manager,
2203 message_sender: &scripted_scene.message_sender,
2204 message_dispatcher: &mut scripted_scene.message_dispatcher,
2205 task_pool: &mut self.task_pool,
2206 graphics_context: &mut self.graphics_context,
2207 user_interfaces: &mut self.user_interfaces,
2208 script_index: node_task_handler.script_index,
2209 input_state: &self.input_state,
2210 };
2211 try_enqueue_plugin_error(
2212 "handle_async_tasks",
2213 (node_task_handler.closure)(
2214 payload,
2215 script.deref_mut(),
2216 &mut ctx,
2217 ),
2218 &mut self.error_queue,
2219 );
2220
2221 if let Ok(node) =
2222 scene.graph.try_get_node_mut(node_task_handler.node_handle)
2223 {
2224 if let Some(entry) =
2225 node.scripts.get_mut(node_task_handler.script_index)
2226 {
2227 if entry.should_be_deleted {
2228 Log::verify(scene.graph.script_message_sender.send(
2229 NodeScriptMessage::DestroyScript {
2230 script,
2231 handle: node_task_handler.node_handle,
2232 script_index: node_task_handler.script_index,
2233 },
2234 ));
2235 } else {
2236 entry.script = Some(script);
2237 }
2238 }
2239 }
2240 }
2241 }
2242 }
2243 }
2244 }
2245 }
2246 }
2247
2248 fn update_plugins(&mut self, dt: f32, controller: ApplicationLoopController, lag: &mut f32) {
2249 let time = instant::Instant::now();
2250
2251 if self.plugins_enabled {
2252 self.handle_async_tasks(dt, controller, lag);
2254
2255 let mut context = PluginContext {
2257 scenes: &mut self.scenes,
2258 resource_manager: &self.resource_manager,
2259 graphics_context: &mut self.graphics_context,
2260 dt,
2261 lag,
2262 user_interfaces: &mut self.user_interfaces,
2263 serialization_context: &self.serialization_context,
2264 widget_constructors: &self.widget_constructors,
2265 dyn_type_constructors: &self.dyn_type_constructors,
2266 performance_statistics: &self.performance_statistics,
2267 elapsed_time: self.elapsed_time,
2268 script_processor: &self.script_processor,
2269 async_scene_loader: &mut self.async_scene_loader,
2270 loop_controller: controller,
2271 task_pool: &mut self.task_pool,
2272 input_state: &self.input_state,
2273 };
2274
2275 for plugin in self.plugins.iter_mut() {
2276 try_enqueue_plugin_error(
2277 "update",
2278 plugin.update(&mut context),
2279 &mut self.error_queue,
2280 );
2281 }
2282
2283 let mut uis = self
2284 .user_interfaces
2285 .pair_iter()
2286 .map(|(h, _)| h)
2287 .collect::<VecDeque<_>>();
2288
2289 while let Some(ui) = uis.pop_front() {
2290 while let Some(message) = self
2291 .user_interfaces
2292 .try_get_mut(ui)
2293 .ok()
2294 .and_then(|ui| ui.poll_message())
2295 {
2296 let mut context = PluginContext {
2297 scenes: &mut self.scenes,
2298 resource_manager: &self.resource_manager,
2299 graphics_context: &mut self.graphics_context,
2300 dt,
2301 lag,
2302 user_interfaces: &mut self.user_interfaces,
2303 serialization_context: &self.serialization_context,
2304 widget_constructors: &self.widget_constructors,
2305 dyn_type_constructors: &self.dyn_type_constructors,
2306 performance_statistics: &self.performance_statistics,
2307 elapsed_time: self.elapsed_time,
2308 script_processor: &self.script_processor,
2309 async_scene_loader: &mut self.async_scene_loader,
2310 loop_controller: controller,
2311 task_pool: &mut self.task_pool,
2312 input_state: &self.input_state,
2313 };
2314
2315 for plugin in self.plugins.iter_mut() {
2316 try_enqueue_plugin_error(
2317 "on_ui_message",
2318 plugin.on_ui_message(&mut context, &message, ui),
2319 &mut self.error_queue,
2320 );
2321 }
2322 }
2323 }
2324 }
2325
2326 self.performance_statistics.plugins_time = instant::Instant::now() - time;
2327 }
2328
2329 fn post_update_plugins(
2330 &mut self,
2331 dt: f32,
2332 controller: ApplicationLoopController,
2333 lag: &mut f32,
2334 ) {
2335 let time = instant::Instant::now();
2336
2337 if self.plugins_enabled {
2338 let mut context = PluginContext {
2339 scenes: &mut self.scenes,
2340 resource_manager: &self.resource_manager,
2341 graphics_context: &mut self.graphics_context,
2342 dt,
2343 lag,
2344 user_interfaces: &mut self.user_interfaces,
2345 serialization_context: &self.serialization_context,
2346 widget_constructors: &self.widget_constructors,
2347 dyn_type_constructors: &self.dyn_type_constructors,
2348 performance_statistics: &self.performance_statistics,
2349 elapsed_time: self.elapsed_time,
2350 script_processor: &self.script_processor,
2351 async_scene_loader: &mut self.async_scene_loader,
2352 loop_controller: controller,
2353 task_pool: &mut self.task_pool,
2354 input_state: &self.input_state,
2355 };
2356
2357 for plugin in self.plugins.iter_mut() {
2358 try_enqueue_plugin_error(
2359 "post_update",
2360 plugin.post_update(&mut context),
2361 &mut self.error_queue,
2362 );
2363 }
2364
2365 while let Some(container) = self.error_queue.pop_back() {
2366 for plugin in self.plugins.iter_mut() {
2367 if plugin.on_game_error(&mut context, &container.error) {
2368 continue;
2369 }
2370 match container.source {
2371 GameErrorSource::PluginMethod(method_name) => {
2372 err!(
2373 "An error occurred in {method_name} plugin method. Reason: {}",
2374 container.error
2375 );
2376 }
2377 GameErrorSource::ScriptMethod {
2378 scene_handle,
2379 node_handle,
2380 method_name,
2381 } => {
2382 let node_name = context
2383 .scenes
2384 .try_get(scene_handle)
2385 .ok()
2386 .and_then(|scene| {
2387 scene.graph.try_get(node_handle).ok().map(|n| n.name())
2388 })
2389 .unwrap_or("<undefined>");
2390
2391 err!(
2392 "An error occurred during {method_name} call in {node_handle} \
2393 node (name: {node_name}). Reason: {}",
2394 container.error
2395 );
2396 }
2397 }
2398 }
2399 }
2400 }
2401
2402 self.performance_statistics.plugins_time += instant::Instant::now() - time;
2403 }
2404
2405 pub fn handle_os_events(
2407 &mut self,
2408 event: &Event<()>,
2409 dt: f32,
2410 controller: ApplicationLoopController,
2411 lag: &mut f32,
2412 ) {
2413 match event {
2414 Event::WindowEvent { event, .. } => match event {
2415 WindowEvent::KeyboardInput { event, .. } => {
2416 let keyboard = &mut self.input_state.keyboard;
2417
2418 match event.state {
2419 ElementState::Pressed => {
2420 if keyboard
2421 .keys
2422 .get(&event.physical_key)
2423 .is_none_or(|state| *state == ElementState::Released)
2424 {
2425 keyboard.pressed_keys.insert(event.physical_key);
2426 }
2427 }
2428 ElementState::Released => {
2429 if keyboard
2430 .keys
2431 .get(&event.physical_key)
2432 .is_some_and(|state| *state == ElementState::Pressed)
2433 {
2434 keyboard.released_keys.insert(event.physical_key);
2435 }
2436 }
2437 }
2438
2439 keyboard.keys.insert(event.physical_key, event.state);
2440 }
2441 WindowEvent::CursorMoved { position, .. } => {
2442 self.input_state.mouse.position =
2443 Vector2::new(position.x as f32, position.y as f32);
2444 }
2445 _ => (),
2446 },
2447 Event::DeviceEvent { event, .. } => match event {
2448 DeviceEvent::MouseMotion { delta } => {
2449 self.input_state.mouse.speed = Vector2::new(delta.0 as f32, delta.1 as f32);
2450 }
2451 DeviceEvent::Button { button, state } => {
2452 let mouse = &mut self.input_state.mouse;
2453
2454 match *state {
2455 ElementState::Pressed => {
2456 if mouse
2457 .buttons_state
2458 .get(button)
2459 .is_none_or(|state| *state == ElementState::Released)
2460 {
2461 mouse.pressed_buttons.insert(*button);
2462 }
2463 }
2464 ElementState::Released => {
2465 if mouse
2466 .buttons_state
2467 .get(button)
2468 .is_some_and(|state| *state == ElementState::Pressed)
2469 {
2470 mouse.released_buttons.insert(*button);
2471 }
2472 }
2473 }
2474
2475 mouse.buttons_state.insert(*button, *state);
2476 }
2477 _ => (),
2478 },
2479 _ => (),
2480 }
2481
2482 if self.plugins_enabled {
2483 for plugin in self.plugins.iter_mut() {
2484 let ctx = PluginContext {
2485 scenes: &mut self.scenes,
2486 resource_manager: &self.resource_manager,
2487 graphics_context: &mut self.graphics_context,
2488 dt,
2489 lag,
2490 user_interfaces: &mut self.user_interfaces,
2491 serialization_context: &self.serialization_context,
2492 widget_constructors: &self.widget_constructors,
2493 dyn_type_constructors: &self.dyn_type_constructors,
2494 performance_statistics: &self.performance_statistics,
2495 elapsed_time: self.elapsed_time,
2496 script_processor: &self.script_processor,
2497 async_scene_loader: &mut self.async_scene_loader,
2498 loop_controller: controller,
2499 task_pool: &mut self.task_pool,
2500 input_state: &self.input_state,
2501 };
2502
2503 try_enqueue_plugin_error(
2504 "on_os_event",
2505 plugin.on_os_event(event, ctx),
2506 &mut self.error_queue,
2507 );
2508 }
2509 }
2510 }
2511
2512 pub(crate) fn handle_graphics_context_created_by_plugins(
2513 &mut self,
2514 dt: f32,
2515 controller: ApplicationLoopController,
2516 lag: &mut f32,
2517 ) {
2518 if self.plugins_enabled {
2519 for plugin in self.plugins.iter_mut() {
2520 let ctx = PluginContext {
2521 scenes: &mut self.scenes,
2522 resource_manager: &self.resource_manager,
2523 graphics_context: &mut self.graphics_context,
2524 dt,
2525 lag,
2526 user_interfaces: &mut self.user_interfaces,
2527 serialization_context: &self.serialization_context,
2528 widget_constructors: &self.widget_constructors,
2529 dyn_type_constructors: &self.dyn_type_constructors,
2530 performance_statistics: &self.performance_statistics,
2531 elapsed_time: self.elapsed_time,
2532 script_processor: &self.script_processor,
2533 async_scene_loader: &mut self.async_scene_loader,
2534 loop_controller: controller,
2535 task_pool: &mut self.task_pool,
2536 input_state: &self.input_state,
2537 };
2538
2539 try_enqueue_plugin_error(
2540 "on_graphics_context_initialized",
2541 plugin.on_graphics_context_initialized(ctx),
2542 &mut self.error_queue,
2543 );
2544 }
2545 }
2546 }
2547
2548 pub(crate) fn handle_graphics_context_destroyed_by_plugins(
2549 &mut self,
2550 dt: f32,
2551 controller: ApplicationLoopController,
2552 lag: &mut f32,
2553 ) {
2554 if self.plugins_enabled {
2555 for plugin in self.plugins.iter_mut() {
2556 let ctx = PluginContext {
2557 scenes: &mut self.scenes,
2558 resource_manager: &self.resource_manager,
2559 graphics_context: &mut self.graphics_context,
2560 dt,
2561 lag,
2562 user_interfaces: &mut self.user_interfaces,
2563 serialization_context: &self.serialization_context,
2564 widget_constructors: &self.widget_constructors,
2565 dyn_type_constructors: &self.dyn_type_constructors,
2566 performance_statistics: &self.performance_statistics,
2567 elapsed_time: self.elapsed_time,
2568 script_processor: &self.script_processor,
2569 async_scene_loader: &mut self.async_scene_loader,
2570 loop_controller: controller,
2571 task_pool: &mut self.task_pool,
2572 input_state: &self.input_state,
2573 };
2574
2575 try_enqueue_plugin_error(
2576 "on_graphics_context_destroyed",
2577 plugin.on_graphics_context_destroyed(ctx),
2578 &mut self.error_queue,
2579 );
2580 }
2581 }
2582 }
2583
2584 pub(crate) fn handle_before_rendering_by_plugins(
2585 &mut self,
2586 dt: f32,
2587 controller: ApplicationLoopController,
2588 lag: &mut f32,
2589 ) {
2590 if self.plugins_enabled {
2591 for plugin in self.plugins.iter_mut() {
2592 let ctx = PluginContext {
2593 scenes: &mut self.scenes,
2594 resource_manager: &self.resource_manager,
2595 graphics_context: &mut self.graphics_context,
2596 dt,
2597 lag,
2598 user_interfaces: &mut self.user_interfaces,
2599 serialization_context: &self.serialization_context,
2600 widget_constructors: &self.widget_constructors,
2601 dyn_type_constructors: &self.dyn_type_constructors,
2602 performance_statistics: &self.performance_statistics,
2603 elapsed_time: self.elapsed_time,
2604 script_processor: &self.script_processor,
2605 async_scene_loader: &mut self.async_scene_loader,
2606 loop_controller: controller,
2607 task_pool: &mut self.task_pool,
2608 input_state: &self.input_state,
2609 };
2610
2611 try_enqueue_plugin_error(
2612 "before_rendering",
2613 plugin.before_rendering(ctx),
2614 &mut self.error_queue,
2615 );
2616 }
2617 }
2618 }
2619
2620 pub(crate) fn handle_os_event_by_scripts(
2628 &mut self,
2629 event: &Event<()>,
2630 scene_handle: Handle<Scene>,
2631 dt: f32,
2632 ) {
2633 if let Some(scripted_scene) = self
2634 .script_processor
2635 .scripted_scenes
2636 .iter_mut()
2637 .find(|s| s.handle == scene_handle)
2638 {
2639 let scene = &mut self.scenes[scene_handle];
2640 if *scene.enabled {
2641 process_scripts(
2642 "on_os_event",
2643 scene,
2644 scene_handle,
2645 &mut self.plugins,
2646 &self.resource_manager,
2647 &scripted_scene.message_sender,
2648 &mut scripted_scene.message_dispatcher,
2649 &mut self.task_pool,
2650 &mut self.graphics_context,
2651 &mut self.user_interfaces,
2652 dt,
2653 self.elapsed_time,
2654 &self.input_state,
2655 &mut self.error_queue,
2656 |script, context| {
2657 if script.initialized && script.started {
2658 script.on_os_event(event, context)
2659 } else {
2660 Ok(())
2661 }
2662 },
2663 )
2664 }
2665 }
2666 }
2667
2668 pub fn handle_model_events(&mut self) {
2673 while let Ok(event) = self.model_events_receiver.try_recv() {
2674 if let ResourceEvent::Reloaded(resource) = event {
2675 if let Some(model) = resource.try_cast::<Model>() {
2676 Log::info(format!(
2677 "A model resource {} was reloaded, propagating changes...",
2678 model.resource_uuid()
2679 ));
2680
2681 ResourceDependencyGraph::new(model, self.resource_manager.clone()).resolve();
2683
2684 Log::info("Propagating changes to active scenes...");
2685
2686 for scene in self.scenes.iter_mut() {
2690 scene.resolve();
2691 }
2692 }
2693 }
2694 }
2695 }
2696
2697 #[inline]
2700 pub fn render(&mut self) -> Result<(), FrameworkError> {
2701 for ui in self.user_interfaces.iter_mut() {
2702 ui.set_time(self.elapsed_time);
2703 }
2704
2705 if let GraphicsContext::Initialized(ref mut ctx) = self.graphics_context {
2706 ctx.renderer.render_and_swap_buffers(
2707 &self.scenes,
2708 self.elapsed_time,
2709 self.user_interfaces
2710 .iter_mut()
2711 .filter(|ui| match ui.render_mode {
2712 RenderMode::EveryFrame => true,
2713 RenderMode::OnChanges => ui.need_render,
2714 })
2715 .map(|ui| {
2716 ui.need_render = false;
2717 UiRenderInfo {
2718 ui,
2719 render_target: ui.render_target.clone(),
2720 clear_color: Default::default(),
2721 resource_manager: &self.resource_manager,
2722 }
2723 }),
2724 &ctx.window,
2725 &self.resource_manager,
2726 )?;
2727 }
2728
2729 Ok(())
2730 }
2731
2732 pub(crate) fn enable_plugins(
2734 &mut self,
2735 scene_path: Option<&str>,
2736 enabled: bool,
2737 controller: ApplicationLoopController,
2738 ) {
2739 if self.plugins_enabled != enabled {
2740 self.plugins_enabled = enabled;
2741
2742 if self.plugins_enabled {
2743 for plugin in self.plugins.iter_mut() {
2745 let ctx = PluginContext {
2746 scenes: &mut self.scenes,
2747 resource_manager: &self.resource_manager,
2748 graphics_context: &mut self.graphics_context,
2749 dt: 0.0,
2750 lag: &mut 0.0,
2751 user_interfaces: &mut self.user_interfaces,
2752 serialization_context: &self.serialization_context,
2753 widget_constructors: &self.widget_constructors,
2754 dyn_type_constructors: &self.dyn_type_constructors,
2755 performance_statistics: &self.performance_statistics,
2756 elapsed_time: self.elapsed_time,
2757 script_processor: &self.script_processor,
2758 async_scene_loader: &mut self.async_scene_loader,
2759 loop_controller: controller,
2760 task_pool: &mut self.task_pool,
2761 input_state: &self.input_state,
2762 };
2763
2764 try_enqueue_plugin_error(
2765 "init",
2766 plugin.init(scene_path, ctx),
2767 &mut self.error_queue,
2768 );
2769 }
2770 } else {
2771 self.handle_scripts(0.0);
2772
2773 for mut plugin in self.plugins.drain(..) {
2774 let ctx = PluginContext {
2775 scenes: &mut self.scenes,
2776 resource_manager: &self.resource_manager,
2777 graphics_context: &mut self.graphics_context,
2778 dt: 0.0,
2779 lag: &mut 0.0,
2780 user_interfaces: &mut self.user_interfaces,
2781 serialization_context: &self.serialization_context,
2782 widget_constructors: &self.widget_constructors,
2783 dyn_type_constructors: &self.dyn_type_constructors,
2784 performance_statistics: &self.performance_statistics,
2785 elapsed_time: self.elapsed_time,
2786 script_processor: &self.script_processor,
2787 async_scene_loader: &mut self.async_scene_loader,
2788 loop_controller: controller,
2789 task_pool: &mut self.task_pool,
2790 input_state: &self.input_state,
2791 };
2792
2793 try_enqueue_plugin_error(
2795 "on_deinit",
2796 plugin.on_deinit(ctx),
2797 &mut self.error_queue,
2798 );
2799 }
2800 }
2801 }
2802 }
2803
2804 fn register_plugin_internal(
2805 serialization_context: &Arc<SerializationContext>,
2806 widget_constructors: &Arc<WidgetConstructorContainer>,
2807 dyn_type_constructors: &Arc<DynTypeConstructorContainer>,
2808 resource_manager: &ResourceManager,
2809 plugin: &dyn Plugin,
2810 error_queue: &mut ErrorQueue,
2811 ) {
2812 try_enqueue_plugin_error(
2813 "register",
2814 plugin.register(PluginRegistrationContext {
2815 serialization_context,
2816 widget_constructors,
2817 dyn_type_constructors,
2818 resource_manager,
2819 }),
2820 error_queue,
2821 );
2822 }
2823
2824 fn register_plugin(&mut self, plugin: &dyn Plugin) {
2825 Self::register_plugin_internal(
2826 &self.serialization_context,
2827 &self.widget_constructors,
2828 &self.dyn_type_constructors,
2829 &self.resource_manager,
2830 plugin,
2831 &mut self.error_queue,
2832 )
2833 }
2834
2835 pub fn add_plugin<P>(&mut self, plugin: P)
2837 where
2838 P: Plugin + 'static,
2839 {
2840 self.register_plugin(&plugin);
2841
2842 self.plugins.push(PluginContainer::Static(Box::new(plugin)));
2843 }
2844
2845 pub fn add_dynamic_plugin<P>(
2857 &mut self,
2858 path: P,
2859 reload_when_changed: bool,
2860 use_relative_paths: bool,
2861 ) -> Result<&dyn Plugin, String>
2862 where
2863 P: AsRef<Path> + 'static,
2864 {
2865 Ok(self.add_dynamic_plugin_custom(DyLibDynamicPlugin::new(
2866 path,
2867 reload_when_changed,
2868 use_relative_paths,
2869 )?))
2870 }
2871
2872 pub fn add_dynamic_plugin_custom<P>(&mut self, plugin: P) -> &dyn Plugin
2874 where
2875 P: DynamicPlugin + 'static,
2876 {
2877 let display_name = plugin.display_name();
2878
2879 let plugin_container = PluginContainer::Dynamic(Box::new(plugin));
2880
2881 self.register_plugin(plugin_container.deref());
2882 self.plugins.push(plugin_container);
2883
2884 Log::info(format!("Plugin {display_name:?} was loaded successfully"));
2885
2886 &**self.plugins.last().unwrap()
2887 }
2888
2889 pub fn reload_plugin(
2892 &mut self,
2893 plugin_index: usize,
2894 dt: f32,
2895 controller: ApplicationLoopController,
2896 lag: &mut f32,
2897 ) -> Result<(), String> {
2898 let plugin_container = &mut self.plugins[plugin_index];
2899 let PluginContainer::Dynamic(plugin) = plugin_container else {
2900 return Err(format!(
2901 "Plugin {plugin_index} is static and cannot be reloaded!",
2902 ));
2903 };
2904
2905 if !plugin.is_loaded() {
2906 return Err(format!("Cannot reload unloaded plugin {plugin_index}!"));
2910 }
2911 plugin.prepare_to_reload();
2912
2913 let plugin_type_id = plugin.as_loaded_ref().type_id();
2914 let plugin_assembly_name = plugin.as_loaded_ref().assembly_name();
2915
2916 let mut scenes_state = Vec::new();
2918 for (scene_handle, scene) in self.scenes.pair_iter_mut() {
2919 if let Some(data) = hotreload::SceneState::try_create_from_plugin(
2920 scene_handle,
2921 scene,
2922 &self.serialization_context,
2923 plugin.as_loaded_ref(),
2924 )? {
2925 scenes_state.push(data);
2926 }
2927 }
2928
2929 let mut prefab_scenes = Vec::new();
2931 let rm_state = self.resource_manager.state();
2932 for resource in rm_state.resources().iter() {
2933 if let Some(model) = resource.try_cast::<Model>() {
2934 let mut model_state = model.state();
2935 if let Some(data) = model_state.data() {
2936 if let Some(scene_state) = hotreload::SceneState::try_create_from_plugin(
2937 Handle::NONE,
2938 &mut data.scene,
2939 &self.serialization_context,
2940 plugin.as_loaded_ref(),
2941 )? {
2942 prefab_scenes.push((model.clone(), scene_state));
2943 }
2944 }
2945 }
2946 }
2947 drop(rm_state);
2948
2949 let mut constructors = FxHashSet::default();
2951 for (type_uuid, constructor) in self.serialization_context.script_constructors.map().iter()
2952 {
2953 if constructor.assembly_name == plugin_assembly_name {
2954 constructors.insert(*type_uuid);
2955 }
2956 }
2957 for type_uuid in constructors.iter() {
2958 self.serialization_context
2959 .script_constructors
2960 .remove(*type_uuid);
2961 }
2962
2963 let mut constructors = FxHashSet::default();
2965 for (type_uuid, constructor) in self.serialization_context.node_constructors.map().iter() {
2966 if constructor.assembly_name == plugin_assembly_name {
2967 constructors.insert(*type_uuid);
2968 }
2969 }
2970 for type_uuid in constructors.iter() {
2971 self.serialization_context
2972 .node_constructors
2973 .remove(*type_uuid);
2974 }
2975
2976 let mut constructors = FxHashSet::default();
2978 for (type_uuid, constructor) in self.widget_constructors.map().iter() {
2979 if constructor.assembly_name == plugin_assembly_name {
2980 constructors.insert(*type_uuid);
2981 }
2982 }
2983 for type_uuid in constructors.iter() {
2984 self.widget_constructors.remove(*type_uuid);
2985 }
2986
2987 {
2989 let mut resources_to_reload = FxHashSet::default();
2990 let mut state = self.resource_manager.state();
2991 for resource in state.resources().iter() {
2992 let data = resource.lock();
2993 if let ResourceState::Ok { ref data, .. } = data.state {
2994 data.as_reflect(&mut |reflect| {
2995 if reflect.assembly_name() == plugin_assembly_name {
2996 resources_to_reload.insert(resource.clone());
2997 }
2998 })
2999 }
3000 }
3001
3002 for resource_to_reload in resources_to_reload.iter() {
3003 Log::info(format!(
3004 "Reloading {:?} resource, because it is used in plugin {plugin_assembly_name}",
3005 state.resource_path(resource_to_reload)
3006 ));
3007
3008 state.reload_resource(resource_to_reload.clone());
3009 }
3010
3011 drop(state);
3012
3013 block_on(join_all(resources_to_reload));
3014 }
3015
3016 if let GraphicsContext::Initialized(ref mut graphics_context) = self.graphics_context {
3018 let render_passes = graphics_context.renderer.render_passes().to_vec();
3019 for render_pass in render_passes {
3020 if render_pass.borrow().source_type_id() == plugin_type_id {
3021 graphics_context.renderer.remove_render_pass(render_pass);
3022 }
3023 }
3024 }
3025
3026 let mut visitor = hotreload::make_writing_visitor();
3027 plugin
3028 .as_loaded_mut()
3029 .visit("Plugin", &mut visitor)
3030 .map_err(|e| e.to_string())?;
3031 let mut binary_blob = Cursor::new(Vec::<u8>::new());
3032 visitor
3033 .save_binary_to_memory(&mut binary_blob)
3034 .map_err(|e| e.to_string())?;
3035
3036 Log::info(format!(
3037 "Plugin {plugin_index} was serialized successfully!"
3038 ));
3039
3040 drop(visitor);
3045
3046 let binary_blob = binary_blob.into_inner();
3047
3048 plugin.reload(&mut |plugin| {
3049 Self::register_plugin_internal(
3053 &self.serialization_context,
3054 &self.widget_constructors,
3055 &self.dyn_type_constructors,
3056 &self.resource_manager,
3057 plugin,
3058 &mut self.error_queue,
3059 );
3060
3061 self.resource_manager.update_or_load_registry();
3064
3065 let mut visitor = hotreload::make_reading_visitor(
3066 &binary_blob,
3067 &self.serialization_context,
3068 &self.resource_manager,
3069 &self.widget_constructors,
3070 &self.dyn_type_constructors,
3071 )
3072 .map_err(|e| e.to_string())?;
3073
3074 plugin
3075 .visit("Plugin", &mut visitor)
3076 .map_err(|e| e.to_string())?;
3077 Ok(())
3078 })?;
3079
3080 for (model, scene_state) in prefab_scenes {
3082 Log::info(format!(
3083 "Deserializing {} prefab content...",
3084 model.resource_uuid()
3085 ));
3086
3087 scene_state.deserialize_into_prefab_scene(
3088 &model,
3089 &self.serialization_context,
3090 &self.resource_manager,
3091 &self.widget_constructors,
3092 &self.dyn_type_constructors,
3093 )?;
3094 }
3095
3096 for scene_state in scenes_state {
3098 let scene = &mut self.scenes[scene_state.scene];
3099 scene_state.deserialize_into_scene(
3100 scene,
3101 &self.serialization_context,
3102 &self.resource_manager,
3103 &self.widget_constructors,
3104 &self.dyn_type_constructors,
3105 )?;
3106 }
3107
3108 let ctx = PluginContext {
3110 scenes: &mut self.scenes,
3111 resource_manager: &self.resource_manager,
3112 user_interfaces: &mut self.user_interfaces,
3113 graphics_context: &mut self.graphics_context,
3114 dt,
3115 lag,
3116 serialization_context: &self.serialization_context,
3117 widget_constructors: &self.widget_constructors,
3118 dyn_type_constructors: &self.dyn_type_constructors,
3119 performance_statistics: &Default::default(),
3120 elapsed_time: self.elapsed_time,
3121 script_processor: &self.script_processor,
3122 async_scene_loader: &mut self.async_scene_loader,
3123 loop_controller: controller,
3124 task_pool: &mut self.task_pool,
3125 input_state: &self.input_state,
3126 };
3127 try_enqueue_plugin_error(
3128 "on_loaded",
3129 plugin.as_loaded_mut().on_loaded(ctx),
3130 &mut self.error_queue,
3131 );
3132
3133 Log::info(format!("Plugin {plugin_index} was successfully reloaded!"));
3134
3135 Ok(())
3136 }
3137
3138 pub fn plugins(&self) -> &[PluginContainer] {
3140 &self.plugins
3141 }
3142
3143 pub fn plugins_mut(&mut self) -> &mut [PluginContainer] {
3145 &mut self.plugins
3146 }
3147
3148 pub fn reload_dynamic_plugins<F>(
3150 &mut self,
3151 dt: f32,
3152 controller: ApplicationLoopController,
3153 lag: &mut f32,
3154 mut on_reloaded: F,
3155 ) -> Result<(), String>
3156 where
3157 F: FnMut(&dyn Plugin),
3158 {
3159 for plugin_index in 0..self.plugins.len() {
3160 if let PluginContainer::Dynamic(plugin) = &self.plugins[plugin_index] {
3161 if plugin.is_reload_needed_now() {
3162 self.reload_plugin(plugin_index, dt, controller, lag)?;
3163
3164 on_reloaded(self.plugins[plugin_index].deref_mut());
3165 }
3166 }
3167 }
3168
3169 Ok(())
3170 }
3171}
3172
3173impl Drop for Engine {
3174 fn drop(&mut self) {
3175 let scenes = self
3179 .scenes
3180 .pair_iter()
3181 .map(|(h, _)| h)
3182 .collect::<Vec<Handle<Scene>>>();
3183
3184 for handle in scenes {
3185 self.scenes.remove(handle);
3186 }
3187
3188 self.enable_plugins(
3190 None,
3191 false,
3192 ApplicationLoopController::Headless {
3193 running: &Default::default(),
3194 },
3195 );
3196 }
3197}
3198
3199#[cfg(test)]
3200mod test {
3201 use crate::engine::ApplicationLoopController;
3202 use crate::plugin::error::GameResult;
3203 use crate::scene::pivot::Pivot;
3204 use crate::{
3205 asset::manager::ResourceManager,
3206 core::{
3207 pool::Handle, reflect::prelude::*, task::TaskPool, type_traits::prelude::*,
3208 visitor::prelude::*,
3209 },
3210 engine::{task::TaskPoolHandler, GraphicsContext, ScriptProcessor},
3211 graph::SceneGraph,
3212 scene::{base::BaseBuilder, node::Node, pivot::PivotBuilder, Scene, SceneContainer},
3213 script::{
3214 ScriptContext, ScriptDeinitContext, ScriptMessageContext, ScriptMessagePayload,
3215 ScriptTrait,
3216 },
3217 };
3218 use fyrox_resource::io::FsResourceIo;
3219 use fyrox_ui::UiContainer;
3220 use std::cell::Cell;
3221 use std::sync::{
3222 mpsc::{self, Sender, TryRecvError},
3223 Arc,
3224 };
3225
3226 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
3227 struct Source {
3228 node_handle: Handle<Node>,
3229 script_index: usize,
3230 }
3231
3232 impl Source {
3233 fn from_ctx(ctx: &ScriptContext) -> Self {
3234 Self {
3235 node_handle: ctx.handle,
3236 script_index: ctx.script_index,
3237 }
3238 }
3239
3240 fn from_deinit_ctx(ctx: &ScriptDeinitContext) -> Self {
3241 Self {
3242 node_handle: ctx.node_handle,
3243 script_index: ctx.script_index,
3244 }
3245 }
3246
3247 fn from_msg_ctx(ctx: &ScriptMessageContext) -> Self {
3248 Self {
3249 node_handle: ctx.handle,
3250 script_index: ctx.script_index,
3251 }
3252 }
3253 }
3254
3255 #[allow(clippy::enum_variant_names)]
3256 #[derive(PartialEq, Eq, Clone, Debug)]
3257 enum Event {
3258 Initialized(Source),
3259 Started(Source),
3260 Updated(Source),
3261 Destroyed(Source),
3262 EventReceived(Source),
3263 }
3264
3265 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3266 #[type_uuid(id = "2569de84-d4b2-427d-969b-d5c7b31a0ba6")]
3267 struct MyScript {
3268 #[reflect(hidden)]
3269 #[visit(skip)]
3270 sender: Sender<Event>,
3271 spawned: bool,
3272 }
3273
3274 impl ScriptTrait for MyScript {
3275 fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3276 self.sender
3277 .send(Event::Initialized(Source::from_ctx(ctx)))
3278 .unwrap();
3279
3280 let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3282 sender: self.sender.clone(),
3283 }))
3284 .build(&mut ctx.scene.graph);
3285 assert_eq!(handle, Handle::<Pivot>::new(2, 1));
3286
3287 Ok(())
3288 }
3289
3290 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3291 self.sender
3292 .send(Event::Started(Source::from_ctx(ctx)))
3293 .unwrap();
3294
3295 let handle = PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3297 sender: self.sender.clone(),
3298 }))
3299 .build(&mut ctx.scene.graph);
3300 assert_eq!(handle, Handle::<Pivot>::new(3, 1));
3301
3302 Ok(())
3303 }
3304
3305 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3306 self.sender
3307 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3308 .unwrap();
3309
3310 Ok(())
3311 }
3312
3313 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3314 self.sender
3315 .send(Event::Updated(Source::from_ctx(ctx)))
3316 .unwrap();
3317
3318 if !self.spawned {
3319 PivotBuilder::new(BaseBuilder::new().with_script(MySubScript {
3321 sender: self.sender.clone(),
3322 }))
3323 .build(&mut ctx.scene.graph);
3324
3325 self.spawned = true;
3326 }
3327
3328 Ok(())
3329 }
3330 }
3331
3332 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3333 #[type_uuid(id = "1cebacd9-b500-4753-93be-39db344add21")]
3334 struct MySubScript {
3335 #[reflect(hidden)]
3336 #[visit(skip)]
3337 sender: Sender<Event>,
3338 }
3339
3340 impl ScriptTrait for MySubScript {
3341 fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3342 self.sender
3343 .send(Event::Initialized(Source::from_ctx(ctx)))
3344 .unwrap();
3345 Ok(())
3346 }
3347
3348 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3349 self.sender
3350 .send(Event::Started(Source::from_ctx(ctx)))
3351 .unwrap();
3352 Ok(())
3353 }
3354
3355 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3356 self.sender
3357 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3358 .unwrap();
3359 Ok(())
3360 }
3361
3362 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3363 self.sender
3364 .send(Event::Updated(Source::from_ctx(ctx)))
3365 .unwrap();
3366 Ok(())
3367 }
3368 }
3369
3370 #[test]
3371 fn test_order() {
3372 let resource_manager =
3373 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3374 let mut scene = Scene::new();
3375
3376 let (tx, rx) = mpsc::channel();
3377
3378 let node_handle = PivotBuilder::new(
3379 BaseBuilder::new()
3380 .with_script(MyScript {
3381 sender: tx.clone(),
3382 spawned: false,
3383 })
3384 .with_script(MySubScript { sender: tx }),
3385 )
3386 .build(&mut scene.graph)
3387 .to_base();
3388 assert_eq!(node_handle, Handle::<Pivot>::new(1, 1));
3389
3390 let node_handle_0 = Source {
3391 node_handle,
3392 script_index: 0,
3393 };
3394 let node_handle_1 = Source {
3395 node_handle,
3396 script_index: 1,
3397 };
3398
3399 let mut scene_container = SceneContainer::new(Default::default());
3400
3401 let scene_handle = scene_container.add(scene);
3402
3403 let mut script_processor = ScriptProcessor::default();
3404
3405 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3406
3407 let handle_on_init = Source {
3408 node_handle: Handle::new(2, 1),
3409 script_index: 0,
3410 };
3411 let handle_on_start = Source {
3412 node_handle: Handle::new(3, 1),
3413 script_index: 0,
3414 };
3415 let handle_on_update1 = Source {
3416 node_handle: Handle::new(4, 1),
3417 script_index: 0,
3418 };
3419 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3420 let mut gc = GraphicsContext::Uninitialized(Default::default());
3421 let mut user_interfaces = UiContainer::default();
3422
3423 for iteration in 0..3 {
3424 script_processor.handle_scripts(
3425 &mut scene_container,
3426 &mut Vec::new(),
3427 &resource_manager,
3428 &mut task_pool,
3429 &mut gc,
3430 &mut user_interfaces,
3431 0.0,
3432 0.0,
3433 &Default::default(),
3434 &mut Default::default(),
3435 );
3436
3437 match iteration {
3438 0 => {
3439 assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_0)));
3440 assert_eq!(rx.try_recv(), Ok(Event::Initialized(node_handle_1)));
3441 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_init)));
3442 assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_0)));
3443 assert_eq!(rx.try_recv(), Ok(Event::Started(node_handle_1)));
3444 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_init)));
3445 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_start)));
3446 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_start)));
3447 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3448 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3449 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3450 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3451 assert_eq!(rx.try_recv(), Ok(Event::Initialized(handle_on_update1)));
3452 assert_eq!(rx.try_recv(), Ok(Event::Started(handle_on_update1)));
3453 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3454 }
3455 1 => {
3456 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_0)));
3457 assert_eq!(rx.try_recv(), Ok(Event::Updated(node_handle_1)));
3458 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_init)));
3459 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_start)));
3460 assert_eq!(rx.try_recv(), Ok(Event::Updated(handle_on_update1)));
3461 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3462
3463 let graph = &mut scene_container[scene_handle].graph;
3465 graph.remove_node(node_handle);
3466 graph.remove_node(handle_on_init.node_handle);
3467 graph.remove_node(handle_on_start.node_handle);
3468 graph.remove_node(handle_on_update1.node_handle);
3469 }
3470 2 => {
3471 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_0)));
3472 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(node_handle_1)));
3473 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_init)));
3474 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_start)));
3475 assert_eq!(rx.try_recv(), Ok(Event::Destroyed(handle_on_update1)));
3476
3477 assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
3479 }
3480 _ => (),
3481 }
3482 }
3483 }
3484
3485 #[derive(Debug, ScriptMessagePayload)]
3486 enum MyMessage {
3487 Foo(usize),
3488 Bar(String),
3489 }
3490
3491 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3492 #[type_uuid(id = "bf2976ad-f41d-4de6-9a32-b1a293956058")]
3493 struct ScriptListeningToMessages {
3494 index: u32,
3495 #[reflect(hidden)]
3496 #[visit(skip)]
3497 sender: Sender<Event>,
3498 }
3499
3500 impl ScriptTrait for ScriptListeningToMessages {
3501 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3502 ctx.message_dispatcher.subscribe_to::<MyMessage>(ctx.handle);
3503 Ok(())
3504 }
3505
3506 fn on_message(
3507 &mut self,
3508 message: &mut dyn ScriptMessagePayload,
3509 ctx: &mut ScriptMessageContext,
3510 ) -> GameResult {
3511 let typed_message = message.downcast_ref::<MyMessage>().unwrap();
3512 match self.index {
3513 0 => {
3514 if let MyMessage::Foo(num) = typed_message {
3515 assert_eq!(*num, 123);
3516 self.sender
3517 .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3518 .unwrap();
3519 } else {
3520 unreachable!()
3521 }
3522 }
3523 1 => {
3524 if let MyMessage::Bar(string) = typed_message {
3525 assert_eq!(string, "Foobar");
3526 self.sender
3527 .send(Event::EventReceived(Source::from_msg_ctx(ctx)))
3528 .unwrap();
3529 } else {
3530 unreachable!()
3531 }
3532 }
3533 _ => (),
3534 }
3535
3536 self.index += 1;
3537
3538 Ok(())
3539 }
3540 }
3541
3542 #[derive(Debug, Clone, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3543 #[type_uuid(id = "6bcbf9b4-9546-42d3-965a-de055ab85475")]
3544 struct ScriptSendingMessages {
3545 index: u32,
3546 }
3547
3548 impl ScriptTrait for ScriptSendingMessages {
3549 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3550 match self.index {
3551 0 => ctx.message_sender.send_global(MyMessage::Foo(123)),
3552 1 => ctx
3553 .message_sender
3554 .send_global(MyMessage::Bar("Foobar".to_string())),
3555 _ => (),
3556 }
3557 self.index += 1;
3558
3559 Ok(())
3560 }
3561 }
3562
3563 #[test]
3564 fn test_messages() {
3565 let resource_manager =
3566 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3567 let mut scene = Scene::new();
3568
3569 let (tx, rx) = mpsc::channel();
3570
3571 PivotBuilder::new(BaseBuilder::new().with_script(ScriptSendingMessages { index: 0 }))
3572 .build(&mut scene.graph);
3573
3574 let receiver_messages =
3575 PivotBuilder::new(BaseBuilder::new().with_script(ScriptListeningToMessages {
3576 sender: tx,
3577 index: 0,
3578 }))
3579 .build(&mut scene.graph)
3580 .to_base();
3581 let receiver_messages_source = Source {
3582 node_handle: receiver_messages,
3583 script_index: 0,
3584 };
3585
3586 let mut scene_container = SceneContainer::new(Default::default());
3587
3588 let scene_handle = scene_container.add(scene);
3589
3590 let mut script_processor = ScriptProcessor::default();
3591 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3592 let mut gc = GraphicsContext::Uninitialized(Default::default());
3593 let mut user_interfaces = UiContainer::default();
3594
3595 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3596
3597 for iteration in 0..2 {
3598 script_processor.handle_scripts(
3599 &mut scene_container,
3600 &mut Vec::new(),
3601 &resource_manager,
3602 &mut task_pool,
3603 &mut gc,
3604 &mut user_interfaces,
3605 0.0,
3606 0.0,
3607 &Default::default(),
3608 &mut Default::default(),
3609 );
3610
3611 match iteration {
3612 0 => {
3613 assert_eq!(
3614 rx.try_recv(),
3615 Ok(Event::EventReceived(receiver_messages_source))
3616 );
3617 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3618 }
3619 1 => {
3620 assert_eq!(
3621 rx.try_recv(),
3622 Ok(Event::EventReceived(receiver_messages_source))
3623 );
3624 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3625 }
3626 _ => (),
3627 }
3628 }
3629 }
3630
3631 #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3632 #[type_uuid(id = "7bcbf9b4-9546-42d3-965a-de055ab85475")]
3633 pub struct ScriptSpawningAsyncTasks {
3634 num: Option<u32>,
3635 }
3636
3637 impl ScriptTrait for ScriptSpawningAsyncTasks {
3638 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3639 ctx.task_pool.spawn_script_task(
3640 ctx.scene_handle,
3641 ctx.handle,
3642 ctx.script_index,
3643 async move { 123u32 },
3644 |result, script: &mut ScriptSpawningAsyncTasks, _ctx| {
3645 assert_eq!(result, 123u32);
3646 script.num = Some(result);
3647 Ok(())
3648 },
3649 );
3650 Ok(())
3651 }
3652 }
3653
3654 #[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3655 #[type_uuid(id = "8bcbf9b4-9546-42d3-965a-de055ab85475")]
3656 pub struct ScriptWithoutAsyncTasks {}
3657
3658 impl ScriptTrait for ScriptWithoutAsyncTasks {}
3659
3660 #[test]
3661 #[cfg(not(target_os = "macos"))] fn test_async_script_tasks() {
3663 use crate::engine::{Engine, EngineInitParams};
3664
3665 let task_pool = Arc::new(TaskPool::default());
3666 let mut engine = Engine::new(EngineInitParams {
3667 graphics_context_params: Default::default(),
3668 serialization_context: Arc::new(Default::default()),
3669 widget_constructors: Arc::new(Default::default()),
3670 dyn_type_constructors: Arc::new(Default::default()),
3671 resource_manager: ResourceManager::new(Arc::new(FsResourceIo), task_pool.clone()),
3672 task_pool,
3673 })
3674 .unwrap();
3675
3676 let is_running = Cell::new(true);
3677
3678 engine.enable_plugins(
3679 None,
3680 true,
3681 ApplicationLoopController::Headless {
3682 running: &is_running,
3683 },
3684 );
3685
3686 let mut scene = Scene::new();
3687
3688 let handle = PivotBuilder::new(
3689 BaseBuilder::new()
3690 .with_script(ScriptSpawningAsyncTasks { num: None })
3691 .with_script(ScriptWithoutAsyncTasks {}),
3692 )
3693 .build(&mut scene.graph);
3694
3695 let scene_handle = engine.scenes.add(scene);
3696
3697 engine.register_scripted_scene(scene_handle);
3698
3699 let mut time = 0.0;
3701 let dt = 1.0 / 60.0;
3702 let mut lag = 0.0;
3703 while time <= 10.0 {
3704 engine.update(
3705 dt,
3706 ApplicationLoopController::Headless {
3707 running: &is_running,
3708 },
3709 &mut lag,
3710 Default::default(),
3711 );
3712 time += dt;
3713 }
3714
3715 let mut scripts = engine.scenes[scene_handle].graph[handle].scripts();
3717 assert_eq!(
3718 scripts
3719 .next()
3720 .and_then(|s| s.cast::<ScriptSpawningAsyncTasks>()),
3721 Some(&ScriptSpawningAsyncTasks { num: Some(123) })
3722 );
3723 assert_eq!(
3724 scripts
3725 .next()
3726 .and_then(|s| s.cast::<ScriptWithoutAsyncTasks>()),
3727 Some(&ScriptWithoutAsyncTasks {})
3728 );
3729 }
3730
3731 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3732 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3733 pub struct ScriptThatDeletesItself {
3734 #[reflect(hidden)]
3735 #[visit(skip)]
3736 sender: Sender<Event>,
3737 }
3738
3739 impl ScriptTrait for ScriptThatDeletesItself {
3740 fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3741 self.sender
3742 .send(Event::Initialized(Source::from_ctx(ctx)))
3743 .unwrap();
3744 Ok(())
3745 }
3746
3747 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3748 self.sender
3749 .send(Event::Started(Source::from_ctx(ctx)))
3750 .unwrap();
3751 Ok(())
3752 }
3753
3754 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3755 self.sender
3756 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3757 .unwrap();
3758 Ok(())
3759 }
3760
3761 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3762 self.sender
3763 .send(Event::Updated(Source::from_ctx(ctx)))
3764 .unwrap();
3765
3766 let node = &mut ctx.scene.graph[ctx.handle];
3767 node.remove_script(ctx.script_index);
3768 Ok(())
3769 }
3770 }
3771
3772 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3773 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3774 pub struct ScriptThatAddsScripts {
3775 num: usize,
3776 #[reflect(hidden)]
3777 #[visit(skip)]
3778 sender: Sender<Event>,
3779 }
3780
3781 impl ScriptTrait for ScriptThatAddsScripts {
3782 fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3783 self.sender
3784 .send(Event::Initialized(Source::from_ctx(ctx)))
3785 .unwrap();
3786 Ok(())
3787 }
3788
3789 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3790 self.sender
3791 .send(Event::Started(Source::from_ctx(ctx)))
3792 .unwrap();
3793
3794 for i in 0..self.num {
3795 ctx.scene.graph[ctx.handle].add_script(SimpleScript {
3796 stuff: i,
3797 sender: self.sender.clone(),
3798 });
3799 }
3800
3801 Ok(())
3802 }
3803
3804 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3805 self.sender
3806 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3807 .unwrap();
3808
3809 Ok(())
3810 }
3811
3812 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3813 self.sender
3814 .send(Event::Updated(Source::from_ctx(ctx)))
3815 .unwrap();
3816
3817 Ok(())
3818 }
3819 }
3820
3821 #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
3822 #[type_uuid(id = "9bcbf9b4-9546-42d3-965a-de055ab85475")]
3823 pub struct SimpleScript {
3824 stuff: usize,
3825 #[reflect(hidden)]
3826 #[visit(skip)]
3827 sender: Sender<Event>,
3828 }
3829
3830 impl ScriptTrait for SimpleScript {
3831 fn on_init(&mut self, ctx: &mut ScriptContext) -> GameResult {
3832 self.sender
3833 .send(Event::Initialized(Source::from_ctx(ctx)))
3834 .unwrap();
3835 Ok(())
3836 }
3837
3838 fn on_start(&mut self, ctx: &mut ScriptContext) -> GameResult {
3839 self.sender
3840 .send(Event::Started(Source::from_ctx(ctx)))
3841 .unwrap();
3842 Ok(())
3843 }
3844
3845 fn on_deinit(&mut self, ctx: &mut ScriptDeinitContext) -> GameResult {
3846 self.sender
3847 .send(Event::Destroyed(Source::from_deinit_ctx(ctx)))
3848 .unwrap();
3849 Ok(())
3850 }
3851
3852 fn on_update(&mut self, ctx: &mut ScriptContext) -> GameResult {
3853 self.sender
3854 .send(Event::Updated(Source::from_ctx(ctx)))
3855 .unwrap();
3856 Ok(())
3857 }
3858 }
3859
3860 #[test]
3861 fn test_script_adding_removing() {
3862 let resource_manager =
3863 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
3864 let mut scene = Scene::new();
3865
3866 let (tx, rx) = mpsc::channel();
3867
3868 let node_handle = PivotBuilder::new(
3869 BaseBuilder::new()
3870 .with_script(ScriptThatDeletesItself { sender: tx.clone() })
3871 .with_script(ScriptThatAddsScripts { num: 2, sender: tx }),
3872 )
3873 .build(&mut scene.graph)
3874 .to_base();
3875 assert_eq!(node_handle, Handle::<Node>::new(1, 1));
3876
3877 let mut scene_container = SceneContainer::new(Default::default());
3878
3879 let scene_handle = scene_container.add(scene);
3880
3881 let mut script_processor = ScriptProcessor::default();
3882
3883 script_processor.register_scripted_scene(scene_handle, &resource_manager);
3884
3885 let mut task_pool = TaskPoolHandler::new(Arc::new(TaskPool::new()));
3886 let mut gc = GraphicsContext::Uninitialized(Default::default());
3887 let mut user_interfaces = UiContainer::default();
3888
3889 for iteration in 0..2 {
3890 script_processor.handle_scripts(
3891 &mut scene_container,
3892 &mut Vec::new(),
3893 &resource_manager,
3894 &mut task_pool,
3895 &mut gc,
3896 &mut user_interfaces,
3897 0.0,
3898 0.0,
3899 &Default::default(),
3900 &mut Default::default(),
3901 );
3902
3903 match iteration {
3904 0 => {
3905 for i in 0..2 {
3906 assert_eq!(
3907 rx.try_recv(),
3908 Ok(Event::Initialized(Source {
3909 node_handle,
3910 script_index: i,
3911 }))
3912 );
3913 }
3914 for i in 0..2 {
3915 assert_eq!(
3916 rx.try_recv(),
3917 Ok(Event::Started(Source {
3918 node_handle,
3919 script_index: i,
3920 }))
3921 );
3922 }
3923 for i in 2..4 {
3924 assert_eq!(
3925 rx.try_recv(),
3926 Ok(Event::Initialized(Source {
3927 node_handle,
3928 script_index: i,
3929 }))
3930 );
3931 }
3932 for i in 2..4 {
3933 assert_eq!(
3934 rx.try_recv(),
3935 Ok(Event::Started(Source {
3936 node_handle,
3937 script_index: i,
3938 }))
3939 );
3940 }
3941 for i in 0..4 {
3942 assert_eq!(
3943 rx.try_recv(),
3944 Ok(Event::Updated(Source {
3945 node_handle,
3946 script_index: i,
3947 }))
3948 );
3949 }
3950 assert_eq!(
3951 rx.try_recv(),
3952 Ok(Event::Destroyed(Source {
3953 node_handle,
3954 script_index: 0,
3955 }))
3956 );
3957
3958 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3959 }
3960 1 => {
3961 for i in 0..3 {
3962 assert_eq!(
3963 rx.try_recv(),
3964 Ok(Event::Updated(Source {
3965 node_handle,
3966 script_index: i,
3967 }))
3968 );
3969 }
3970
3971 assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
3972 }
3973 _ => (),
3974 }
3975 }
3976 }
3977}