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}