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