fyrox_impl/script/mod.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![warn(missing_docs)]
22
23//! Script is used to add custom logic to scene nodes. See [ScriptTrait] for more info.
24
25use crate::{
26 asset::manager::ResourceManager,
27 core::{
28 log::Log,
29 pool::Handle,
30 reflect::{FieldInfo, Reflect, ReflectArray, ReflectList},
31 type_traits::ComponentProvider,
32 uuid::Uuid,
33 visitor::{Visit, VisitResult, Visitor},
34 TypeUuidProvider,
35 },
36 engine::{task::TaskPoolHandler, GraphicsContext, ScriptMessageDispatcher},
37 event::Event,
38 gui::UiContainer,
39 plugin::{Plugin, PluginContainer},
40 scene::{base::NodeScriptMessage, node::Node, Scene},
41};
42use std::{
43 any::{Any, TypeId},
44 fmt::{Debug, Formatter},
45 ops::{Deref, DerefMut},
46 str::FromStr,
47 sync::mpsc::Sender,
48};
49
50pub mod constructor;
51
52pub(crate) trait UniversalScriptContext {
53 fn node(&mut self) -> Option<&mut Node>;
54 fn destroy_script_deferred(&self, script: Script, index: usize);
55 fn set_script_index(&mut self, index: usize);
56}
57
58/// A script message's payload.
59pub trait ScriptMessagePayload: Any + Send + Debug {
60 /// Returns `self` as `&dyn Any`
61 fn as_any_ref(&self) -> &dyn Any;
62
63 /// Returns `self` as `&dyn Any`
64 fn as_any_mut(&mut self) -> &mut dyn Any;
65}
66
67impl dyn ScriptMessagePayload {
68 /// Tries to cast the payload to a particular type.
69 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
70 self.as_any_ref().downcast_ref::<T>()
71 }
72
73 /// Tries to cast the payload to a particular type.
74 pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
75 self.as_any_mut().downcast_mut::<T>()
76 }
77}
78
79impl<T> ScriptMessagePayload for T
80where
81 T: 'static + Send + Debug,
82{
83 fn as_any_ref(&self) -> &dyn Any {
84 self
85 }
86
87 fn as_any_mut(&mut self) -> &mut dyn Any {
88 self
89 }
90}
91
92/// Defines how a script message will be delivered for each node in a hierarchy.
93#[derive(Debug)]
94pub enum RoutingStrategy {
95 /// An message will be passed to the specified root node and then to every node up in the hierarchy.
96 Up,
97 /// An message will be passed to every node down the tree in the hierarchy.
98 Down,
99}
100
101/// A script message of a particular kind.
102#[derive(Debug)]
103pub struct ScriptMessage {
104 /// Actual message payload.
105 pub payload: Box<dyn ScriptMessagePayload>,
106 /// Actual script message kind.
107 pub kind: ScriptMessageKind,
108}
109
110/// An message for a node with a script.
111#[derive(Debug)]
112pub enum ScriptMessageKind {
113 /// An message for a specific scene node. It will be delivered only if the node is subscribed to receive
114 /// messages of a particular type.
115 Targeted(Handle<Node>),
116
117 /// An message for a hierarchy of nodes.
118 Hierarchical {
119 /// Starting node in a scene graph. Message will be delivered to each node in hierarchy in the order
120 /// defined by `routing` if the node is subscribed to receive messages of a particular type.
121 root: Handle<Node>,
122
123 /// [Routing strategy](RoutingStrategy) for the message.
124 routing: RoutingStrategy,
125 },
126
127 /// An message that will be delivered for **every** scene node that is subscribed to receive messages
128 /// of a particular type.
129 Global,
130}
131
132/// A script message sender.
133#[derive(Clone)]
134pub struct ScriptMessageSender {
135 pub(crate) sender: Sender<ScriptMessage>,
136}
137
138impl Debug for ScriptMessageSender {
139 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140 write!(f, "ScriptMessageSender")
141 }
142}
143
144impl ScriptMessageSender {
145 /// Send a generic script message.
146 pub fn send(&self, message: ScriptMessage) {
147 if self.sender.send(message).is_err() {
148 Log::err("Failed to send script message, it means the scene is already deleted!");
149 }
150 }
151
152 /// Sends a targeted script message with the given payload.
153 pub fn send_to_target<T>(&self, target: Handle<Node>, payload: T)
154 where
155 T: ScriptMessagePayload,
156 {
157 self.send(ScriptMessage {
158 payload: Box::new(payload),
159 kind: ScriptMessageKind::Targeted(target),
160 })
161 }
162
163 /// Sends a global script message with the given payload.
164 pub fn send_global<T>(&self, payload: T)
165 where
166 T: ScriptMessagePayload,
167 {
168 self.send(ScriptMessage {
169 payload: Box::new(payload),
170 kind: ScriptMessageKind::Global,
171 })
172 }
173
174 /// Sends a hierarchical script message with the given payload.
175 pub fn send_hierarchical<T>(&self, root: Handle<Node>, routing: RoutingStrategy, payload: T)
176 where
177 T: ScriptMessagePayload,
178 {
179 self.send(ScriptMessage {
180 payload: Box::new(payload),
181 kind: ScriptMessageKind::Hierarchical { root, routing },
182 })
183 }
184}
185
186/// Base script trait is used to automatically implement some trait to reduce amount of boilerplate code.
187pub trait BaseScript: Visit + Reflect + Send + Debug + 'static {
188 /// Creates exact copy of the script.
189 fn clone_box(&self) -> Box<dyn ScriptTrait>;
190
191 /// Casts self as `Any`
192 fn as_any_ref(&self) -> &dyn Any;
193
194 /// Casts self as `Any`
195 fn as_any_ref_mut(&mut self) -> &mut dyn Any;
196
197 /// Script instance type UUID. The value will be used for serialization, to write type
198 /// identifier to a data source so the engine can restore the script from data source.
199 ///
200 /// # Important notes
201 ///
202 /// Do **not** use [`Uuid::new_v4`] or any other [`Uuid`] methods that generates ids, ids
203 /// generated using these methods are **random** and are not suitable for serialization!
204 ///
205 /// # Example
206 ///
207 /// All you need to do in the method is to return `Self::type_uuid`.
208 ///
209 /// ```rust
210 /// use std::str::FromStr;
211 /// use fyrox_impl::{
212 /// core::visitor::prelude::*,
213 /// core::reflect::prelude::*,
214 /// core::uuid::Uuid,
215 /// script::ScriptTrait,
216 /// core::TypeUuidProvider,
217 /// core::uuid::uuid, core::type_traits::prelude::*
218 /// };
219 ///
220 /// #[derive(Reflect, Visit, Debug, Clone, ComponentProvider)]
221 /// struct MyScript { }
222 ///
223 /// // Implement TypeUuidProvider trait that will return type uuid of the type.
224 /// // Every script must implement the trait so the script can be registered in
225 /// // serialization context of the engine.
226 /// impl TypeUuidProvider for MyScript {
227 /// fn type_uuid() -> Uuid {
228 /// // Use https://www.uuidgenerator.net/ to generate new UUID.
229 /// uuid!("4cfbe65e-a2c1-474f-b123-57516d80b1f8")
230 /// }
231 /// }
232 ///
233 /// impl ScriptTrait for MyScript { }
234 /// ```
235 fn id(&self) -> Uuid;
236}
237
238impl<T> BaseScript for T
239where
240 T: Clone + ScriptTrait + Any + TypeUuidProvider,
241{
242 fn clone_box(&self) -> Box<dyn ScriptTrait> {
243 Box::new(self.clone())
244 }
245
246 fn as_any_ref(&self) -> &dyn Any {
247 self
248 }
249
250 fn as_any_ref_mut(&mut self) -> &mut dyn Any {
251 self
252 }
253
254 fn id(&self) -> Uuid {
255 T::type_uuid()
256 }
257}
258
259/// A simple wrapper for a reference to plugins container. It has some useful methods to fetch
260/// a plugin of certain type. See [`PluginsRefMut::of_type_ref`] and [`PluginsRefMut::of_type_mut`].
261pub struct PluginsRefMut<'a>(pub &'a mut [PluginContainer]);
262
263impl Deref for PluginsRefMut<'_> {
264 type Target = [PluginContainer];
265
266 fn deref(&self) -> &Self::Target {
267 self.0
268 }
269}
270
271impl DerefMut for PluginsRefMut<'_> {
272 fn deref_mut(&mut self) -> &mut Self::Target {
273 self.0
274 }
275}
276
277impl PluginsRefMut<'_> {
278 /// Searches for a plugin of the given type `T`.
279 #[inline]
280 pub fn of_type_ref<T>(&self) -> Option<&T>
281 where
282 T: Plugin,
283 {
284 self.0.iter().find_map(|p| p.cast::<T>())
285 }
286
287 /// Searches for a plugin of the given type `T`.
288 #[inline]
289 pub fn of_type_mut<T>(&mut self) -> Option<&mut T>
290 where
291 T: Plugin,
292 {
293 self.0.iter_mut().find_map(|p| p.cast_mut::<T>())
294 }
295
296 /// Searches for a plugin of the given type `T`. Panics if there's no such plugin.
297 #[inline]
298 pub fn get<T>(&self) -> &T
299 where
300 T: Plugin,
301 {
302 self.of_type_ref().unwrap()
303 }
304
305 /// Searches for a plugin of the given type `T`. Panics if there's no such plugin.
306 #[inline]
307 pub fn get_mut<T>(&mut self) -> &mut T
308 where
309 T: Plugin,
310 {
311 self.of_type_mut().unwrap()
312 }
313}
314
315/// A set of data, that provides contextual information for script methods.
316pub struct ScriptContext<'a, 'b, 'c> {
317 /// Amount of time that passed from last call. It has valid values only when called from `on_update`.
318 pub dt: f32,
319
320 /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
321 /// this value is **not** guaranteed to match real time. A user can change delta time with
322 /// which the engine "ticks" and this delta time affects elapsed time.
323 pub elapsed_time: f32,
324
325 /// A slice of references to all registered plugins. For example you can store some "global" data
326 /// in a plugin and access it later in scripts. A simplest example would be something like this:
327 ///
328 /// ```rust
329 /// # use fyrox_impl::{
330 /// # core::{reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*},
331 /// # plugin::Plugin,
332 /// # script::{ScriptContext, ScriptTrait},
333 /// # };
334 /// #
335 /// #[derive(Visit, Reflect, Default, Debug)]
336 /// struct Game {
337 /// player_name: String,
338 /// }
339 ///
340 /// impl Plugin for Game {}
341 ///
342 /// #[derive(Visit, Reflect, Clone, Default, Debug, TypeUuidProvider, ComponentProvider)]
343 /// #[type_uuid(id = "f732654e-5e3c-4b52-9a3d-44c0cfb14e18")]
344 /// struct MyScript {}
345 ///
346 /// impl ScriptTrait for MyScript {
347 /// fn on_update(&mut self, ctx: &mut ScriptContext) {
348 /// let game = ctx.plugins.get::<Game>();
349 ///
350 /// println!("Player name is: {}", game.player_name);
351 /// }
352 /// }
353 /// ```
354 ///
355 /// Since player name usually a sort of a "global" variable, it can be stored in the plugin itself,
356 /// and to access the plugin all you need to do is to call `ctx.plugins.get::<Game>()`. You can
357 /// also mutate any data in a plugin by getting the mutable reference via `get_mut()` method.
358 pub plugins: PluginsRefMut<'a>,
359
360 /// Handle of a node to which the script instance belongs to. To access the node itself use `scene` field:
361 ///
362 /// ```rust
363 /// # use fyrox_impl::script::ScriptContext;
364 /// # fn foo(context: ScriptContext) {
365 /// let node_mut = &mut context.scene.graph[context.handle];
366 /// # }
367 /// ```
368 pub handle: Handle<Node>,
369
370 /// A reference to a scene the script instance belongs to. You have full mutable access to scene content
371 /// in most of the script methods.
372 pub scene: &'b mut Scene,
373
374 /// A handle of a scene the script instance belongs to.
375 pub scene_handle: Handle<Scene>,
376
377 /// A reference to resource manager, use it to load resources.
378 pub resource_manager: &'a ResourceManager,
379
380 /// An message sender. Every message sent via this sender will be then passed to every [`ScriptTrait::on_message`]
381 /// method of every script.
382 pub message_sender: &'c ScriptMessageSender,
383
384 /// A message dispatcher. If you need to receive messages of a particular type, you must subscribe to a type
385 /// explicitly. See [`ScriptTrait::on_message`] for more examples.
386 pub message_dispatcher: &'c mut ScriptMessageDispatcher,
387
388 /// Task pool for asynchronous task management.
389 pub task_pool: &'a mut TaskPoolHandler,
390
391 /// Current graphics context of the engine. See [`GraphicsContext`] docs for more info.
392 pub graphics_context: &'a mut GraphicsContext,
393
394 /// A reference to user interface container of the engine. The engine guarantees that there's
395 /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
396 /// get a reference to it.
397 pub user_interfaces: &'a mut UiContainer,
398
399 /// Index of the script. Never save this index, it is only valid while this context exists!
400 pub script_index: usize,
401}
402
403impl UniversalScriptContext for ScriptContext<'_, '_, '_> {
404 fn node(&mut self) -> Option<&mut Node> {
405 self.scene.graph.try_get_mut(self.handle)
406 }
407
408 fn destroy_script_deferred(&self, script: Script, index: usize) {
409 Log::verify(
410 self.scene
411 .graph
412 .script_message_sender
413 .send(NodeScriptMessage::DestroyScript {
414 script,
415 handle: self.handle,
416 script_index: index,
417 }),
418 )
419 }
420
421 fn set_script_index(&mut self, index: usize) {
422 self.script_index = index;
423 }
424}
425
426/// A set of data, that provides contextual information for script methods.
427pub struct ScriptMessageContext<'a, 'b, 'c> {
428 /// Amount of time that passed from last call. It has valid values only when called from `on_update`.
429 pub dt: f32,
430
431 /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
432 /// this value is **not** guaranteed to match real time. A user can change delta time with
433 /// which the engine "ticks" and this delta time affects elapsed time.
434 pub elapsed_time: f32,
435
436 /// A slice of references to all registered plugins. For example you can store some "global" data
437 /// in a plugin and access it later in scripts. See examples in the [`ScriptContext`] struct.
438 pub plugins: PluginsRefMut<'a>,
439
440 /// Handle of a node to which the script instance belongs to. To access the node itself use `scene` field:
441 ///
442 /// ```rust
443 /// # use fyrox_impl::script::ScriptContext;
444 /// # fn foo(context: ScriptContext) {
445 /// let node_mut = &mut context.scene.graph[context.handle];
446 /// # }
447 /// ```
448 pub handle: Handle<Node>,
449
450 /// A reference to a scene the script instance belongs to. You have full mutable access to scene content
451 /// in most of the script methods.
452 pub scene: &'b mut Scene,
453
454 /// A handle of a scene the script instance belongs to.
455 pub scene_handle: Handle<Scene>,
456
457 /// A reference to resource manager, use it to load resources.
458 pub resource_manager: &'a ResourceManager,
459
460 /// An message sender. Every message sent via this sender will be then passed to every [`ScriptTrait::on_message`]
461 /// method of every script.
462 pub message_sender: &'c ScriptMessageSender,
463
464 /// Task pool for asynchronous task management.
465 pub task_pool: &'a mut TaskPoolHandler,
466
467 /// Current graphics context of the engine. See [`GraphicsContext`] docs for more info.
468 pub graphics_context: &'a mut GraphicsContext,
469
470 /// A reference to user interface container of the engine. The engine guarantees that there's
471 /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
472 /// get a reference to it.
473 pub user_interfaces: &'a mut UiContainer,
474
475 /// Index of the script. Never save this index, it is only valid while this context exists!
476 pub script_index: usize,
477}
478
479impl UniversalScriptContext for ScriptMessageContext<'_, '_, '_> {
480 fn node(&mut self) -> Option<&mut Node> {
481 self.scene.graph.try_get_mut(self.handle)
482 }
483
484 fn destroy_script_deferred(&self, script: Script, index: usize) {
485 Log::verify(
486 self.scene
487 .graph
488 .script_message_sender
489 .send(NodeScriptMessage::DestroyScript {
490 script,
491 handle: self.handle,
492 script_index: index,
493 }),
494 )
495 }
496
497 fn set_script_index(&mut self, index: usize) {
498 self.script_index = index;
499 }
500}
501
502/// A set of data that will be passed to a script instance just before its destruction.
503pub struct ScriptDeinitContext<'a, 'b, 'c> {
504 /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
505 /// this value is **not** guaranteed to match real time. A user can change delta time with
506 /// which the engine "ticks" and this delta time affects elapsed time.
507 pub elapsed_time: f32,
508
509 /// A slice of references to all registered plugins. For example you can store some "global" data
510 /// in a plugin and access it later in scripts. See examples in the [`ScriptContext`] struct.
511 pub plugins: PluginsRefMut<'a>,
512
513 /// A reference to resource manager, use it to load resources.
514 pub resource_manager: &'a ResourceManager,
515
516 /// A reference to a scene the script instance was belonging to. You have full mutable access to scene content
517 /// in most of the script methods.
518 pub scene: &'b mut Scene,
519
520 /// A handle of a scene the script instance belongs to.
521 pub scene_handle: Handle<Scene>,
522
523 /// Handle to a parent scene node. Use it with caution because parent node could be deleted already and
524 /// any unchecked borrowing using the handle will cause panic!
525 pub node_handle: Handle<Node>,
526
527 /// An message sender. Every message sent via this sender will be then passed to every [`ScriptTrait::on_message`]
528 /// method of every script.
529 pub message_sender: &'c ScriptMessageSender,
530
531 /// Task pool for asynchronous task management.
532 pub task_pool: &'a mut TaskPoolHandler,
533
534 /// Current graphics context of the engine. See [`GraphicsContext`] docs for more info.
535 pub graphics_context: &'a mut GraphicsContext,
536
537 /// A reference to user interface container of the engine. The engine guarantees that there's
538 /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
539 /// get a reference to it.
540 pub user_interfaces: &'a mut UiContainer,
541
542 /// Index of the script. Never save this index, it is only valid while this context exists!
543 pub script_index: usize,
544}
545
546impl UniversalScriptContext for ScriptDeinitContext<'_, '_, '_> {
547 fn node(&mut self) -> Option<&mut Node> {
548 self.scene.graph.try_get_mut(self.node_handle)
549 }
550
551 fn destroy_script_deferred(&self, script: Script, index: usize) {
552 Log::verify(
553 self.scene
554 .graph
555 .script_message_sender
556 .send(NodeScriptMessage::DestroyScript {
557 script,
558 handle: self.node_handle,
559 script_index: index,
560 }),
561 )
562 }
563
564 fn set_script_index(&mut self, index: usize) {
565 self.script_index = index;
566 }
567}
568
569/// Script is a set predefined methods that are called on various stages by the engine. It is used to add
570/// custom behaviour to game entities.
571pub trait ScriptTrait: BaseScript + ComponentProvider {
572 /// The method is called when the script wasn't initialized yet. It is guaranteed to be called once,
573 /// and before any other methods of the script.
574 ///
575 /// # Important
576 ///
577 /// The method **will not** be called in case if you serialized initialized script instance and then
578 /// loaded the instance. Internal flag will tell the engine that the script is initialized and this
579 /// method **will not** be called. This is intentional design decision to be able to create save files
580 /// in games. If you need a method that will be called in any case, use [`ScriptTrait::on_start`].
581 fn on_init(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) {}
582
583 /// The method is called after [`ScriptTrait::on_init`], but in separate pass, which means that all
584 /// script instances are already initialized. However, if implementor of this method creates a new
585 /// node with a script, there will be a second pass of initialization. The method is guaranteed to
586 /// be called once.
587 fn on_start(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) {}
588
589 /// The method is called when the script is about to be destroyed. It is guaranteed to be called last.
590 fn on_deinit(&mut self, #[allow(unused_variables)] ctx: &mut ScriptDeinitContext) {}
591
592 /// Called when there is an event from the OS. The method allows you to "listen" for events
593 /// coming from the main window of your game. It could be used to react to pressed keys, mouse movements,
594 /// etc.
595 fn on_os_event(
596 &mut self,
597 #[allow(unused_variables)] event: &Event<()>,
598 #[allow(unused_variables)] ctx: &mut ScriptContext,
599 ) {
600 }
601
602 /// Performs a single update tick of the script. The method may be called multiple times per frame, but it is guaranteed
603 /// that the rate of call is stable and by default it will be called 60 times per second, but can be changed by using
604 /// [`crate::engine::executor::Executor::set_desired_update_rate`] method.
605 fn on_update(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) {}
606
607 /// Allows you to react to certain script messages. It could be used for communication between scripts; to
608 /// bypass borrowing issues. If you need to receive messages of a particular type, you must subscribe to a type
609 /// explicitly. Usually it is done in [`ScriptTrait::on_start`] method:
610 ///
611 /// ```rust
612 /// use fyrox_impl::{
613 /// core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*, type_traits::prelude::*},
614 /// core::TypeUuidProvider,
615 /// script::ScriptTrait,
616 /// script::{ScriptContext, ScriptMessageContext, ScriptMessagePayload},
617 /// };
618 ///
619 /// struct Message;
620 ///
621 /// #[derive(Reflect, Visit, Debug, Clone, ComponentProvider)]
622 /// struct MyScript {}
623 ///
624 /// # impl TypeUuidProvider for MyScript {
625 /// # fn type_uuid() -> Uuid {
626 /// # todo!();
627 /// # }
628 /// # }
629 ///
630 /// impl ScriptTrait for MyScript {
631 /// fn on_start(&mut self, ctx: &mut ScriptContext) {
632 /// // Subscription is mandatory to receive any message of the type!
633 /// ctx.message_dispatcher.subscribe_to::<Message>(ctx.handle)
634 /// }
635 ///
636 /// fn on_message(
637 /// &mut self,
638 /// message: &mut dyn ScriptMessagePayload,
639 /// ctx: &mut ScriptMessageContext,
640 /// ) {
641 /// if let Some(message) = message.downcast_ref::<Message>() {
642 /// // Do something.
643 /// }
644 /// }
645 /// }
646 /// ```
647 fn on_message(
648 &mut self,
649 #[allow(unused_variables)] message: &mut dyn ScriptMessagePayload,
650 #[allow(unused_variables)] ctx: &mut ScriptMessageContext,
651 ) {
652 }
653}
654
655/// A wrapper for actual script instance internals, it used by the engine.
656#[derive(Debug)]
657pub struct Script {
658 instance: Box<dyn ScriptTrait>,
659 pub(crate) initialized: bool,
660 pub(crate) started: bool,
661}
662
663impl TypeUuidProvider for Script {
664 fn type_uuid() -> Uuid {
665 Uuid::from_str("24ecd17d-9b46-4cc8-9d07-a1273e50a20e").unwrap()
666 }
667}
668
669impl Reflect for Script {
670 fn source_path() -> &'static str {
671 file!()
672 }
673
674 fn type_name(&self) -> &'static str {
675 self.instance.type_name()
676 }
677
678 fn doc(&self) -> &'static str {
679 self.instance.doc()
680 }
681
682 fn assembly_name(&self) -> &'static str {
683 self.instance.assembly_name()
684 }
685
686 fn type_assembly_name() -> &'static str {
687 env!("CARGO_PKG_NAME")
688 }
689
690 fn fields_info(&self, func: &mut dyn FnMut(&[FieldInfo])) {
691 self.instance.fields_info(func)
692 }
693
694 fn into_any(self: Box<Self>) -> Box<dyn Any> {
695 self.instance.into_any()
696 }
697
698 fn as_any(&self, func: &mut dyn FnMut(&dyn Any)) {
699 self.instance.deref().as_any(func)
700 }
701
702 fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn Any)) {
703 self.instance.deref_mut().as_any_mut(func)
704 }
705
706 fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
707 self.instance.deref().as_reflect(func)
708 }
709
710 fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
711 self.instance.deref_mut().as_reflect_mut(func)
712 }
713
714 fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
715 self.instance.deref_mut().set(value)
716 }
717
718 fn fields(&self, func: &mut dyn FnMut(&[&dyn Reflect])) {
719 self.instance.deref().fields(func)
720 }
721
722 fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [&mut dyn Reflect])) {
723 self.instance.deref_mut().fields_mut(func)
724 }
725
726 fn field(&self, name: &str, func: &mut dyn FnMut(Option<&dyn Reflect>)) {
727 self.instance.deref().field(name, func)
728 }
729
730 fn field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut dyn Reflect>)) {
731 self.instance.deref_mut().field_mut(name, func)
732 }
733
734 fn as_array(&self, func: &mut dyn FnMut(Option<&dyn ReflectArray>)) {
735 self.instance.deref().as_array(func)
736 }
737
738 fn as_array_mut(&mut self, func: &mut dyn FnMut(Option<&mut dyn ReflectArray>)) {
739 self.instance.deref_mut().as_array_mut(func)
740 }
741
742 fn as_list(&self, func: &mut dyn FnMut(Option<&dyn ReflectList>)) {
743 self.instance.deref().as_list(func)
744 }
745
746 fn as_list_mut(&mut self, func: &mut dyn FnMut(Option<&mut dyn ReflectList>)) {
747 self.instance.deref_mut().as_list_mut(func)
748 }
749}
750
751impl Deref for Script {
752 type Target = dyn ScriptTrait;
753
754 fn deref(&self) -> &Self::Target {
755 &*self.instance
756 }
757}
758
759impl DerefMut for Script {
760 fn deref_mut(&mut self) -> &mut Self::Target {
761 &mut *self.instance
762 }
763}
764
765impl Visit for Script {
766 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
767 let mut region_guard = visitor.enter_region(name)?;
768
769 // Check for new format first, this branch will fail only on attempt to deserialize
770 // scripts in old format.
771 if self.instance.visit("Data", &mut region_guard).is_ok() {
772 // Visit flags.
773 self.initialized.visit("Initialized", &mut region_guard)?;
774 } else {
775 Log::warn(format!(
776 "Unable to load script instance of id {} in new format! Trying to load in old format...",
777 self.id()
778 ));
779
780 // Leave region and try to load in old format.
781 drop(region_guard);
782
783 self.instance.visit(name, visitor)?;
784
785 Log::warn(format!(
786 "Script instance of id {} loaded successfully using compatibility loader! Resave the script!",
787 self.id()
788 ));
789 }
790
791 Ok(())
792 }
793}
794
795impl Clone for Script {
796 fn clone(&self) -> Self {
797 Self {
798 instance: self.instance.clone_box(),
799 initialized: false,
800 started: false,
801 }
802 }
803}
804
805impl Script {
806 /// Creates new script wrapper using given script instance.
807 #[inline]
808 pub fn new<T: ScriptTrait>(script_object: T) -> Self {
809 Self {
810 instance: Box::new(script_object),
811 initialized: false,
812 started: false,
813 }
814 }
815
816 /// Performs downcasting to a particular type.
817 #[inline]
818 pub fn cast<T: ScriptTrait>(&self) -> Option<&T> {
819 self.instance.deref().as_any_ref().downcast_ref::<T>()
820 }
821
822 /// Performs downcasting to a particular type.
823 #[inline]
824 pub fn cast_mut<T: ScriptTrait>(&mut self) -> Option<&mut T> {
825 self.instance
826 .deref_mut()
827 .as_any_ref_mut()
828 .downcast_mut::<T>()
829 }
830
831 /// Tries to borrow a component of given type.
832 #[inline]
833 pub fn query_component_ref<T: Any>(&self) -> Option<&T> {
834 self.instance
835 .query_component_ref(TypeId::of::<T>())
836 .and_then(|c| c.downcast_ref())
837 }
838
839 /// Tries to borrow a component of given type.
840 #[inline]
841 pub fn query_component_mut<T: Any>(&mut self) -> Option<&mut T> {
842 self.instance
843 .query_component_mut(TypeId::of::<T>())
844 .and_then(|c| c.downcast_mut())
845 }
846}
847
848#[cfg(test)]
849mod test {
850 use crate::scene::base::ScriptRecord;
851 use crate::{
852 core::{
853 impl_component_provider, reflect::prelude::*, variable::try_inherit_properties,
854 variable::InheritableVariable, visitor::prelude::*,
855 },
856 scene::base::Base,
857 script::{Script, ScriptTrait},
858 };
859 use fyrox_core::uuid_provider;
860
861 #[derive(Reflect, Visit, Debug, Clone, Default)]
862 struct MyScript {
863 field: InheritableVariable<f32>,
864 }
865
866 impl_component_provider!(MyScript);
867 uuid_provider!(MyScript = "eed9bf56-7d71-44a0-ba8e-0f3163c59669");
868
869 impl ScriptTrait for MyScript {}
870
871 #[test]
872 fn test_script_property_inheritance_on_nodes() {
873 let mut child = Base::default();
874
875 child.scripts.push(ScriptRecord::new(Script::new(MyScript {
876 field: InheritableVariable::new_non_modified(1.23),
877 })));
878
879 let mut parent = Base::default();
880
881 parent.scripts.push(ScriptRecord::new(Script::new(MyScript {
882 field: InheritableVariable::new_non_modified(3.21),
883 })));
884
885 child.as_reflect_mut(&mut |child| {
886 parent.as_reflect(&mut |parent| {
887 try_inherit_properties(child, parent, &[]).unwrap();
888 })
889 });
890
891 assert_eq!(
892 *child.script(0).unwrap().cast::<MyScript>().unwrap().field,
893 3.21
894 );
895 }
896
897 #[test]
898 fn test_script_property_inheritance() {
899 let mut child = Script::new(MyScript {
900 field: InheritableVariable::new_non_modified(1.23),
901 });
902
903 let parent = Script::new(MyScript {
904 field: InheritableVariable::new_non_modified(3.21),
905 });
906
907 child.as_reflect_mut(&mut |child| {
908 parent.as_reflect(&mut |parent| {
909 try_inherit_properties(child, parent, &[]).unwrap();
910 })
911 });
912
913 assert_eq!(*child.cast::<MyScript>().unwrap().field, 3.21);
914 }
915
916 #[test]
917 fn test_script_property_inheritance_option() {
918 let mut child = Some(Script::new(MyScript {
919 field: InheritableVariable::new_non_modified(1.23),
920 }));
921
922 let parent = Some(Script::new(MyScript {
923 field: InheritableVariable::new_non_modified(3.21),
924 }));
925
926 child.as_reflect_mut(&mut |child| {
927 parent.as_reflect(&mut |parent| {
928 try_inherit_properties(child, parent, &[]).unwrap();
929 })
930 });
931
932 assert_eq!(
933 *child.as_ref().unwrap().cast::<MyScript>().unwrap().field,
934 3.21
935 );
936 }
937}