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}