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