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}