Skip to main content

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