fyrox-impl 1.0.1

Feature-rich, easy-to-use, 2D/3D game engine with a scene editor. Like Godot, but in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! Everything related to plugins. See [`Plugin`] docs for more info.

#![warn(missing_docs)]

pub mod dylib;
pub mod error;

use crate::{
    asset::{manager::ResourceManager, untyped::UntypedResource},
    core::{
        define_as_any_trait, dyntype::DynTypeConstructorContainer, log::Log, pool::Handle,
        reflect::Reflect, variable::try_inherit_properties, visitor::error::VisitError,
        visitor::Visit,
    },
    engine::{
        input::InputState, task::TaskPoolHandler, ApplicationLoopController, GraphicsContext,
        PerformanceStatistics, ScriptProcessor, SerializationContext,
    },
    event::Event,
    graph::NodeMapping,
    gui::{
        constructor::WidgetConstructorContainer,
        inspector::editors::PropertyEditorDefinitionContainer, message::UiMessage, UiContainer,
        UserInterface,
    },
    plugin::error::{GameError, GameResult},
    resource::model::Model,
    scene::{graph::NodePool, navmesh, Scene, SceneContainer, SceneLoader},
};
use fyrox_core::err;
use std::path::{Path, PathBuf};
use std::{
    any::TypeId,
    ops::{Deref, DerefMut},
    sync::Arc,
};

/// A wrapper for various plugin types.
pub enum PluginContainer {
    /// Statically linked plugin. Such plugins are meant to be used in final builds, to maximize
    /// performance of the game.
    Static(Box<dyn Plugin>),
    /// Dynamically linked plugin. Such plugins are meant to be used in development mode for rapid
    /// prototyping.
    Dynamic(Box<dyn DynamicPlugin>),
}

/// Abstraction over different kind of plugins that can be reloaded on the fly (whatever it mean).
/// The instance is polled by engine with `is_reload_needed_now()` time to time. if it returns true,
/// then engine serializes current plugin state, then calls `unload()` and then calls `load()`
pub trait DynamicPlugin {
    /// returns human-redable short description of the plugin
    fn display_name(&self) -> String;

    /// engine polls is time to time to determine if it's time to reload plugin
    fn is_reload_needed_now(&self) -> bool;

    /// panics if not loaded
    fn as_loaded_ref(&self) -> &dyn Plugin;

    /// panics if not loaded
    fn as_loaded_mut(&mut self) -> &mut dyn Plugin;

    /// returns false if something bad happends during `reload`.
    /// has no much use except prevention of error spamming
    fn is_loaded(&self) -> bool;

    /// called before saving state and detaching related objects
    fn prepare_to_reload(&mut self) {}

    /// called after plugin-related objects are detached
    /// `fill_and_register` callback exposes plugin instance to engine to register constructors and restore the state
    /// callback approach allows plugins to do some necessary actions right after plugin is registed
    fn reload(
        &mut self,
        fill_and_register: &mut dyn FnMut(&mut dyn Plugin) -> Result<(), String>,
    ) -> Result<(), String>;
}

impl Deref for PluginContainer {
    type Target = dyn Plugin;

    fn deref(&self) -> &Self::Target {
        match self {
            PluginContainer::Static(plugin) => &**plugin,
            PluginContainer::Dynamic(plugin) => plugin.as_loaded_ref(),
        }
    }
}

impl DerefMut for PluginContainer {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            PluginContainer::Static(plugin) => &mut **plugin,
            PluginContainer::Dynamic(plugin) => plugin.as_loaded_mut(),
        }
    }
}

/// Output data of a loader.
pub struct LoaderOutput<T> {
    /// The object produced by the loader.
    pub payload: T,
    /// A path of the source file.
    pub path: PathBuf,
    /// A raw data from which the loader output was created from. Usually it is just a content of
    /// the source file.
    pub data: Vec<u8>,
}

/// Alias for `LoaderOutput<Scene>`;
pub type SceneLoaderOutput = LoaderOutput<Scene>;

/// Alias for `Result<SceneLoaderOutput, VisitError>`;
pub type SceneLoaderResult = Result<SceneLoaderOutput, VisitError>;

/// Alias for `LoaderOutput<UserInterface>`
pub type UiLoaderOutput = LoaderOutput<UserInterface>;

/// Alias for `Result<UiLoaderOutput, VisitError>`
pub type UiLoaderResult = Result<UiLoaderOutput, VisitError>;

/// Contains plugin environment for the registration stage.
pub struct PluginRegistrationContext<'a> {
    /// A reference to serialization context of the engine. See [`SerializationContext`] for more
    /// info.
    pub serialization_context: &'a Arc<SerializationContext>,
    /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
    /// info.
    pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
    /// A container with constructors for dynamic types. See [`DynTypeConstructorContainer`] for more
    /// info.
    pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,
    /// A reference to the resource manager instance of the engine. Could be used to register resource loaders.
    pub resource_manager: &'a ResourceManager,
}

/// Contains plugin environment.
pub struct PluginContext<'a, 'b> {
    /// A reference to scene container of the engine. You can add new scenes from [`Plugin`] methods
    /// by using [`SceneContainer::add`].
    pub scenes: &'a mut SceneContainer,

    /// A reference to the resource manager, it can be used to load various resources and manage
    /// them. See [`ResourceManager`] docs for more info.
    pub resource_manager: &'a ResourceManager,

    /// A reference to user interface container of the engine. The engine guarantees that there's
    /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
    /// get a reference to it.
    pub user_interfaces: &'a mut UiContainer,

    /// A reference to the graphics_context, it contains a reference to the window and the current renderer.
    /// It could be [`GraphicsContext::Uninitialized`] if your application is suspended (possible only on
    /// Android; it is safe to call [`GraphicsContext::as_initialized_ref`] or [`GraphicsContext::as_initialized_mut`]
    /// on every other platform).
    pub graphics_context: &'a mut GraphicsContext,

    /// The time (in seconds) that passed since last call of a method in which the context was
    /// passed. It has fixed value that is defined by a caller (in most cases it is `Executor`).
    pub dt: f32,

    /// A reference to time accumulator, that holds remaining amount of time that should be used
    /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus
    /// stabilizes update rate. The main use of this variable, is to be able to reset `lag` when
    /// you doing some heavy calculations in a your game loop (i.e. loading a new level) so the
    /// engine won't try to "catch up" with all the time that was spent in heavy calculation.
    pub lag: &'b mut f32,

    /// A reference to serialization context of the engine. See [`SerializationContext`] for more
    /// info.
    pub serialization_context: &'a Arc<SerializationContext>,

    /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
    /// info.
    pub widget_constructors: &'a Arc<WidgetConstructorContainer>,

    /// A container with constructors for dynamic types. See [`DynTypeConstructorContainer`] for more
    /// info.
    pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,

    /// Performance statistics from the last frame.
    pub performance_statistics: &'a PerformanceStatistics,

    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
    /// this value is **not** guaranteed to match real time. A user can change delta time with
    /// which the engine "ticks" and this delta time affects elapsed time.
    pub elapsed_time: f32,

    /// Script processor is used to run script methods in a strict order.
    pub script_processor: &'a ScriptProcessor,

    /// Special field that associates the main application event loop (not game loop) with OS-specific
    /// windows. It also can be used to alternate control flow of the application. `None` if the
    /// engine is running in headless mode.
    pub loop_controller: ApplicationLoopController<'b>,

    /// Task pool for asynchronous task management.
    pub task_pool: &'a mut TaskPoolHandler,

    /// A stored state of most common input events. It is used a "shortcut" in cases where event-based
    /// approach is too verbose. It may be useful in simple scenarios where you just need to know
    /// if a button (on keyboard, mouse) was pressed and do something.
    ///
    /// **Important:** this structure does not track from which device the corresponding event has
    /// come from, if you have more than one keyboard and/or mouse, use event-based approach instead!
    pub input_state: &'a InputState,
}

impl<'a, 'b> PluginContext<'a, 'b> {
    /// Spawns an asynchronous task that tries to load a user interface from the given path.
    /// When the task is completed, the specified callback is called that can be used to
    /// modify the UI. The loaded UI must be registered in the engine, otherwise it will be
    /// discarded.
    ///
    /// ## Example
    ///
    /// ```rust
    /// # use fyrox_impl::{
    /// #     core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
    /// #     event::Event,
    /// #     plugin::{error::GameResult, Plugin, PluginContext, PluginRegistrationContext},
    /// #     scene::Scene,
    /// # };
    /// # use std::str::FromStr;
    ///
    /// #[derive(Default, Visit, Reflect, Debug)]
    /// #[reflect(non_cloneable)]
    /// struct MyGame {}
    ///
    /// impl Plugin for MyGame {
    ///     fn init(&mut self, _scene_path: Option<&str>, mut ctx: PluginContext) -> GameResult {
    ///         ctx.load_ui("data/my.ui", |result, game: &mut MyGame, mut ctx| {
    ///             // The loaded UI must be registered in the engine.
    ///             ctx.user_interfaces.add(result?.payload);
    ///             Ok(())
    ///         });
    ///         Ok(())
    ///     }
    /// }
    /// ```
    pub fn load_ui<U, P, C>(&mut self, path: U, callback: C)
    where
        U: Into<PathBuf>,
        P: Plugin,
        for<'c, 'd> C:
            FnOnce(UiLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
    {
        let path = path.into();
        self.task_pool.spawn_plugin_task(
            UserInterface::load_from_file(
                path.clone(),
                self.widget_constructors.clone(),
                self.dyn_type_constructors.clone(),
                self.resource_manager.clone(),
            ),
            move |result, plugin, ctx| match result {
                Ok((ui, data)) => callback(
                    Ok(LoaderOutput {
                        payload: ui,
                        data,
                        path,
                    }),
                    plugin,
                    ctx,
                ),
                Err(e) => callback(Err(e), plugin, ctx),
            },
        );
    }

    /// Tries to load a game scene at the given path.
    ///
    /// This method has a special flag `is_derived` which dictates how to load the scene:
    ///
    /// - `false` - the scene is loaded as-is and returned to the caller. Use this option if you
    ///   don't want to use built-in "saved game" system. See the next option for details.
    /// - `true`, then the requested scene is loaded and a new model resource is
    ///   registered in the resource manager that references the scene source file. Then the loaded
    ///   scene is _cloned_ and all its nodes links to their originals in the source file. Then this
    ///   cloned and processed scene ("derived") is returned. This process essentially links the
    ///   scene to its source file, so when the derived scene is saved to disk, it does not save all
    ///   its content, but only changes from the original scene. Derived scenes are used to create
    ///   saved games. Keep in mind, if you've created a derived scene and saved it, you must load
    ///   this saved game with `is_derived` set to `false`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # use fyrox_impl::{
    /// #     core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
    /// #     event::Event,
    /// #     plugin::{error::GameResult, SceneLoaderResult, Plugin, PluginContext, PluginRegistrationContext},
    /// #     scene::Scene,
    /// # };
    /// # use std::str::FromStr;
    /// #
    /// #[derive(Default, Visit, Reflect, Debug)]
    /// #[reflect(non_cloneable)]
    /// struct MyGame {}
    ///
    /// impl MyGame {
    ///     fn on_scene_loading_result(&mut self, result: SceneLoaderResult, ctx: &mut PluginContext) -> GameResult {
    ///         // Register the scene.
    ///         ctx.scenes.add(result?.payload);
    ///         Ok(())
    ///     }
    /// }
    ///
    /// impl Plugin for MyGame {
    ///     fn init(&mut self, scene_path: Option<&str>, mut ctx: PluginContext) -> GameResult {
    ///         ctx.load_scene(
    ///             scene_path.unwrap_or("data/scene.rgs"),
    ///             false, // See the docs for details.
    ///             |result, game: &mut MyGame, ctx| game.on_scene_loading_result(result, ctx),
    ///         );
    ///         Ok(())
    ///     }
    /// }
    /// ```
    pub fn load_scene<U, P, C>(&mut self, path: U, is_derived: bool, callback: C)
    where
        U: Into<PathBuf>,
        P: Plugin,
        for<'c, 'd> C:
            FnOnce(SceneLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
    {
        let path = path.into();

        let serialization_context = self.serialization_context.clone();
        let dyn_type_constructors = self.dyn_type_constructors.clone();
        let resource_manager = self.resource_manager.clone();
        let uuid = resource_manager.find::<Model>(&path).resource_uuid();
        let io = resource_manager.resource_io();

        self.task_pool.spawn_plugin_task(
            {
                let path = path.clone();
                async move {
                    match SceneLoader::from_file(
                        path,
                        io.as_ref(),
                        serialization_context,
                        dyn_type_constructors,
                        resource_manager.clone(),
                    )
                    .await
                    {
                        Ok((loader, data)) => Ok((loader.finish().await, data)),
                        Err(e) => Err(e),
                    }
                }
            },
            move |result, plugin, ctx| {
                match result {
                    Ok((mut scene, data)) => {
                        if is_derived {
                            let model = ctx.resource_manager.find_uuid::<Model>(uuid);
                            // Create a resource, that will point to the scene we've loaded the
                            // scene from and force scene nodes to inherit data from them.
                            let data = Model {
                                mapping: NodeMapping::UseHandles,
                                // We have to create a full copy of the scene, because otherwise
                                // some methods (`Base::root_resource` in particular) won't work
                                // correctly.
                                scene: scene.clone_one_to_one().0,
                            };
                            model.header().state.commit_ok(data);

                            for (handle, node) in scene.graph.pair_iter_mut() {
                                node.set_inheritance_data(handle, model.clone());
                            }

                            // Reset modified flags in every inheritable property of the scene.
                            // Except nodes, they're inherited in a separate place.
                            (&mut scene as &mut dyn Reflect).apply_recursively_mut(
                                &mut |object| {
                                    let type_id = (*object).type_id();
                                    if type_id != TypeId::of::<NodePool>() {
                                        object.as_inheritable_variable_mut(&mut |variable| {
                                            if let Some(variable) = variable {
                                                variable.reset_modified_flag();
                                            }
                                        });
                                    }
                                },
                                &[
                                    TypeId::of::<UntypedResource>(),
                                    TypeId::of::<navmesh::Container>(),
                                ],
                            )
                        } else {
                            // Take scene data from the source scene.
                            if let Some(source_asset) =
                                scene.graph[scene.graph.get_root()].root_resource()
                            {
                                let source_asset_ref = source_asset.data_ref();
                                let source_scene_ref = &source_asset_ref.scene;
                                Log::verify(try_inherit_properties(
                                    &mut scene,
                                    source_scene_ref,
                                    &[
                                        TypeId::of::<NodePool>(),
                                        TypeId::of::<UntypedResource>(),
                                        TypeId::of::<navmesh::Container>(),
                                    ],
                                ));
                            }
                        }

                        callback(
                            Ok(LoaderOutput {
                                payload: scene,
                                path,
                                data,
                            }),
                            plugin,
                            ctx,
                        )
                    }
                    Err(error) => callback(Err(error), plugin, ctx),
                }
            },
        );
    }

    /// Tries to load either a game scene (via [`Self::load_scene`] or a user interface [`Self::load_ui`].
    /// This method tries to guess the actual type of the asset by comparing the extension in the
    /// path. When a game scene or a UI is loaded, it is added to the respective engine container.
    pub fn load_scene_or_ui<P: Plugin>(&mut self, path: impl AsRef<Path>) {
        let path = path.as_ref();
        let ext = path
            .extension()
            .map(|ext| ext.to_string_lossy().to_string())
            .unwrap_or_default();
        match ext.as_str() {
            "rgs" => self.load_scene(path, false, |result, _: &mut P, ctx| {
                ctx.scenes.add(result?.payload);
                Ok(())
            }),
            "ui" => self.load_ui(path, |result, _: &mut P, ctx| {
                ctx.user_interfaces.add(result?.payload);
                Ok(())
            }),
            _ => err!("File {path:?} is not a game scene nor a user interface!"),
        }
    }
}

define_as_any_trait!(PluginAsAny => Plugin);

impl dyn Plugin {
    /// Performs downcasting to a particular type.
    pub fn cast<T: Plugin>(&self) -> Option<&T> {
        PluginAsAny::as_any(self).downcast_ref::<T>()
    }

    /// Performs downcasting to a particular type.
    pub fn cast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
        PluginAsAny::as_any_mut(self).downcast_mut::<T>()
    }
}

/// Plugin is a convenient interface that allow you to extend engine's functionality.
///
/// # Example
///
/// ```rust
/// # use fyrox_impl::{
/// #     core::{pool::Handle}, core::visitor::prelude::*, core::reflect::prelude::*,
/// #     plugin::{Plugin, PluginContext, PluginRegistrationContext, error::GameResult},
/// #     scene::Scene,
/// #     event::Event
/// # };
/// # use std::str::FromStr;
///
/// #[derive(Default, Visit, Reflect, Debug)]
/// #[reflect(non_cloneable)]
/// struct MyPlugin {}
///
/// impl Plugin for MyPlugin {
///     fn on_deinit(&mut self, context: PluginContext) -> GameResult {
///         // The method is called when the plugin is disabling.
///         // The implementation is optional.
///         Ok(())
///     }
///
///     fn update(&mut self, context: &mut PluginContext) -> GameResult {
///         // The method is called on every frame, it is guaranteed to have fixed update rate.
///         // The implementation is optional.
///         Ok(())
///     }
///
///     fn on_os_event(&mut self, event: &Event<()>, context: PluginContext) -> GameResult {
///         // The method is called when the main window receives an event from the OS.
///         Ok(())
///     }
/// }
/// ```
///
/// # Error Handling
///
/// Every plugin method returns [`GameResult`] (which is a simple wrapper over `Result<(), GameError>`),
/// this helps to reduce the amount of boilerplate code related to error handling. There are a number
/// of errors that can be automatically handled via `?` operator. All supported error types listed
/// in [`error::GameError`] enum.
///
/// The following code snippet shows the most common use cases for error handling:
///
/// ```rust
/// # use fyrox_impl::{
/// #     core::{err, pool::Handle, reflect::prelude::*, visitor::prelude::*},
/// #     event::Event,
/// #     graph::SceneGraph,
/// #     plugin::{error::GameResult, Plugin, PluginContext, PluginRegistrationContext},
/// #     scene::{node::Node, Scene},
/// # };
/// # use std::str::FromStr;
/// #[derive(Default, Visit, Reflect, Debug)]
/// #[reflect(non_cloneable)]
/// struct MyPlugin {
///     scene: Handle<Scene>,
///     player: Handle<Node>,
/// }
///
/// impl Plugin for MyPlugin {
///     fn update(&mut self, context: &mut PluginContext) -> GameResult {
///         // 1. This is the old approach.
///         match context.scenes.try_get(self.scene) {
///             Ok(scene) => match scene.graph.try_get(self.player) {
///                 Ok(player) => {
///                     println!("Player name is: {}", player.name());
///                 }
///                 Err(error) => {
///                     err!("Unable to borrow the player. Reason: {error}")
///                 }
///             },
///             Err(error) => {
///                 err!("Unable to borrow the scene. Reason: {error}")
///             }
///         }
///
///         // 2. This is the same code as above, but with shortcuts for easier error handling.
///         // Message report is will be something like this:
///         // `An error occurred during update plugin method call. Reason: <error message>`.
///         let scene = context.scenes.try_get(self.scene)?;
///         let player = scene.graph.try_get(self.player)?;
///         println!("Player name is: {}", player.name());
///
///         Ok(())
///     }
/// }
/// ```
pub trait Plugin: PluginAsAny + Visit + Reflect {
    /// The method is called when the plugin constructor was just registered in the engine. The main
    /// use of this method is to register scripts and custom scene graph nodes in [`SerializationContext`].
    fn register(
        &self,
        #[allow(unused_variables)] context: PluginRegistrationContext,
    ) -> GameResult {
        Ok(())
    }

    /// This method is used to register property editors for your game types; to make them editable
    /// in the editor.
    fn register_property_editors(
        &self,
        #[allow(unused_variables)] editors: Arc<PropertyEditorDefinitionContainer>,
    ) {
    }

    /// This method is used to initialize your plugin.
    fn init(
        &mut self,
        #[allow(unused_variables)] scene_path: Option<&str>,
        #[allow(unused_variables)] context: PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// This method is called when your plugin was re-loaded from a dynamic library. It could be used
    /// to restore some runtime state, that cannot be serialized. This method is called **only for
    /// dynamic plugins!** It is guaranteed to be called after all plugins were constructed, so the
    /// cross-plugins interactions are possible.
    fn on_loaded(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
        Ok(())
    }

    /// The method is called before plugin will be disabled. It should be used for clean up, or some
    /// additional actions.
    fn on_deinit(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
        Ok(())
    }

    /// Updates the plugin internals at fixed rate (see [`PluginContext::dt`] parameter for more
    /// info).
    fn update(&mut self, #[allow(unused_variables)] context: &mut PluginContext) -> GameResult {
        Ok(())
    }

    /// called after all Plugin and Script updates
    fn post_update(
        &mut self,
        #[allow(unused_variables)] context: &mut PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// The method is called when the main window receives an event from the OS. The main use of
    /// the method is to respond to some external events, for example an event from keyboard or
    /// gamepad. See [`Event`] docs for more info.
    fn on_os_event(
        &mut self,
        #[allow(unused_variables)] event: &Event<()>,
        #[allow(unused_variables)] context: PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// The method is called when a graphics context was successfully created. It could be useful
    /// to catch the moment when it was just created and do something in response.
    fn on_graphics_context_initialized(
        &mut self,
        #[allow(unused_variables)] context: PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// The method is called before the actual frame rendering. It could be useful to render off-screen
    /// data (render something to texture, that can be used later in the main frame).
    fn before_rendering(
        &mut self,
        #[allow(unused_variables)] context: PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// The method is called when the current graphics context was destroyed.
    fn on_graphics_context_destroyed(
        &mut self,
        #[allow(unused_variables)] context: PluginContext,
    ) -> GameResult {
        Ok(())
    }

    /// The method will be called when there is any message from a user interface (UI) instance
    /// of the engine. Use `ui_handle` parameter to find out from which UI the message has come
    /// from.
    fn on_ui_message(
        &mut self,
        #[allow(unused_variables)] context: &mut PluginContext,
        #[allow(unused_variables)] message: &UiMessage,
        #[allow(unused_variables)] ui_handle: Handle<UserInterface>,
    ) -> GameResult {
        Ok(())
    }

    /// This method is called when a game error has occurred, allowing you to perform some
    /// specific action to react to it (for example - to show an error message UI in your game).
    ///
    /// ## Important notes
    ///
    /// This method is called at the end of the current frame, and before that, the engine collects
    /// all the errors into a queue and then processes them one by one. This means that this method
    /// won't be called immediately when an error was returned by any of your plugin or script methods,
    /// but instead the processing will be delayed to the end of the frame.
    ///
    /// The error passed by a reference here instead of by-value, because there could be multiple
    /// plugins that can handle the error. This might seem counterintuitive, but remember that
    /// [`GameError`] can occur during script execution, which is not a part of a plugin and its
    /// methods executed separately, outside the plugin routines.
    ///
    /// ## Error handling
    ///
    /// This method should return `true` if the error was handled and no logging is needed, otherwise
    /// it should return `false` and in this case, the error will be logged by the engine. When
    /// `true` is returned by the plugin, the error won't be passed to any other plugins. By default,
    /// this method returns `false`, which means that it does not handle any errors and the engine
    /// will log the errors as usual.
    fn on_game_error(
        &mut self,
        #[allow(unused_variables)] context: &mut PluginContext,
        #[allow(unused_variables)] error: &GameError,
    ) -> bool {
        false
    }
}