Skip to main content

fyrox_impl/plugin/
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//! Everything related to plugins. See [`Plugin`] docs for more info.
22
23#![warn(missing_docs)]
24
25pub mod dylib;
26pub mod error;
27
28use crate::{
29    asset::{manager::ResourceManager, untyped::UntypedResource},
30    core::{
31        define_as_any_trait, dyntype::DynTypeConstructorContainer, log::Log, pool::Handle,
32        reflect::Reflect, variable::try_inherit_properties, visitor::error::VisitError,
33        visitor::Visit,
34    },
35    engine::{
36        input::InputState, task::TaskPoolHandler, ApplicationLoopController, GraphicsContext,
37        PerformanceStatistics, ScriptProcessor, SerializationContext,
38    },
39    event::Event,
40    graph::NodeMapping,
41    gui::{
42        constructor::WidgetConstructorContainer,
43        inspector::editors::PropertyEditorDefinitionContainer, message::UiMessage, UiContainer,
44        UserInterface,
45    },
46    plugin::error::{GameError, GameResult},
47    resource::model::Model,
48    scene::{graph::NodePool, navmesh, Scene, SceneContainer, SceneLoader},
49};
50use fyrox_core::err;
51use std::path::{Path, PathBuf};
52use std::{
53    any::TypeId,
54    ops::{Deref, DerefMut},
55    sync::Arc,
56};
57
58/// A wrapper for various plugin types.
59pub enum PluginContainer {
60    /// Statically linked plugin. Such plugins are meant to be used in final builds, to maximize
61    /// performance of the game.
62    Static(Box<dyn Plugin>),
63    /// Dynamically linked plugin. Such plugins are meant to be used in development mode for rapid
64    /// prototyping.
65    Dynamic(Box<dyn DynamicPlugin>),
66}
67
68/// Abstraction over different kind of plugins that can be reloaded on the fly (whatever it mean).
69/// The instance is polled by engine with `is_reload_needed_now()` time to time. if it returns true,
70/// then engine serializes current plugin state, then calls `unload()` and then calls `load()`
71pub trait DynamicPlugin {
72    /// returns human-redable short description of the plugin
73    fn display_name(&self) -> String;
74
75    /// engine polls is time to time to determine if it's time to reload plugin
76    fn is_reload_needed_now(&self) -> bool;
77
78    /// panics if not loaded
79    fn as_loaded_ref(&self) -> &dyn Plugin;
80
81    /// panics if not loaded
82    fn as_loaded_mut(&mut self) -> &mut dyn Plugin;
83
84    /// returns false if something bad happends during `reload`.
85    /// has no much use except prevention of error spamming
86    fn is_loaded(&self) -> bool;
87
88    /// called before saving state and detaching related objects
89    fn prepare_to_reload(&mut self) {}
90
91    /// called after plugin-related objects are detached
92    /// `fill_and_register` callback exposes plugin instance to engine to register constructors and restore the state
93    /// callback approach allows plugins to do some necessary actions right after plugin is registed
94    fn reload(
95        &mut self,
96        fill_and_register: &mut dyn FnMut(&mut dyn Plugin) -> Result<(), String>,
97    ) -> Result<(), String>;
98}
99
100impl Deref for PluginContainer {
101    type Target = dyn Plugin;
102
103    fn deref(&self) -> &Self::Target {
104        match self {
105            PluginContainer::Static(plugin) => &**plugin,
106            PluginContainer::Dynamic(plugin) => plugin.as_loaded_ref(),
107        }
108    }
109}
110
111impl DerefMut for PluginContainer {
112    fn deref_mut(&mut self) -> &mut Self::Target {
113        match self {
114            PluginContainer::Static(plugin) => &mut **plugin,
115            PluginContainer::Dynamic(plugin) => plugin.as_loaded_mut(),
116        }
117    }
118}
119
120/// Output data of a loader.
121pub struct LoaderOutput<T> {
122    /// The object produced by the loader.
123    pub payload: T,
124    /// A path of the source file.
125    pub path: PathBuf,
126    /// A raw data from which the loader output was created from. Usually it is just a content of
127    /// the source file.
128    pub data: Vec<u8>,
129}
130
131/// Alias for `LoaderOutput<Scene>`;
132pub type SceneLoaderOutput = LoaderOutput<Scene>;
133
134/// Alias for `Result<SceneLoaderOutput, VisitError>`;
135pub type SceneLoaderResult = Result<SceneLoaderOutput, VisitError>;
136
137/// Alias for `LoaderOutput<UserInterface>`
138pub type UiLoaderOutput = LoaderOutput<UserInterface>;
139
140/// Alias for `Result<UiLoaderOutput, VisitError>`
141pub type UiLoaderResult = Result<UiLoaderOutput, VisitError>;
142
143/// Contains plugin environment for the registration stage.
144pub struct PluginRegistrationContext<'a> {
145    /// A reference to serialization context of the engine. See [`SerializationContext`] for more
146    /// info.
147    pub serialization_context: &'a Arc<SerializationContext>,
148    /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
149    /// info.
150    pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
151    /// A container with constructors for dynamic types. See [`DynTypeConstructorContainer`] for more
152    /// info.
153    pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,
154    /// A reference to the resource manager instance of the engine. Could be used to register resource loaders.
155    pub resource_manager: &'a ResourceManager,
156}
157
158/// Contains plugin environment.
159pub struct PluginContext<'a, 'b> {
160    /// A reference to scene container of the engine. You can add new scenes from [`Plugin`] methods
161    /// by using [`SceneContainer::add`].
162    pub scenes: &'a mut SceneContainer,
163
164    /// A reference to the resource manager, it can be used to load various resources and manage
165    /// them. See [`ResourceManager`] docs for more info.
166    pub resource_manager: &'a ResourceManager,
167
168    /// A reference to user interface container of the engine. The engine guarantees that there's
169    /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
170    /// get a reference to it.
171    pub user_interfaces: &'a mut UiContainer,
172
173    /// A reference to the graphics_context, it contains a reference to the window and the current renderer.
174    /// It could be [`GraphicsContext::Uninitialized`] if your application is suspended (possible only on
175    /// Android; it is safe to call [`GraphicsContext::as_initialized_ref`] or [`GraphicsContext::as_initialized_mut`]
176    /// on every other platform).
177    pub graphics_context: &'a mut GraphicsContext,
178
179    /// The time (in seconds) that passed since last call of a method in which the context was
180    /// passed. It has fixed value that is defined by a caller (in most cases it is `Executor`).
181    pub dt: f32,
182
183    /// A reference to time accumulator, that holds remaining amount of time that should be used
184    /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus
185    /// stabilizes update rate. The main use of this variable, is to be able to reset `lag` when
186    /// you doing some heavy calculations in a your game loop (i.e. loading a new level) so the
187    /// engine won't try to "catch up" with all the time that was spent in heavy calculation.
188    pub lag: &'b mut f32,
189
190    /// A reference to serialization context of the engine. See [`SerializationContext`] for more
191    /// info.
192    pub serialization_context: &'a Arc<SerializationContext>,
193
194    /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
195    /// info.
196    pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
197
198    /// A container with constructors for dynamic types. See [`DynTypeConstructorContainer`] for more
199    /// info.
200    pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,
201
202    /// Performance statistics from the last frame.
203    pub performance_statistics: &'a PerformanceStatistics,
204
205    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
206    /// this value is **not** guaranteed to match real time. A user can change delta time with
207    /// which the engine "ticks" and this delta time affects elapsed time.
208    pub elapsed_time: f32,
209
210    /// Script processor is used to run script methods in a strict order.
211    pub script_processor: &'a ScriptProcessor,
212
213    /// Special field that associates the main application event loop (not game loop) with OS-specific
214    /// windows. It also can be used to alternate control flow of the application. `None` if the
215    /// engine is running in headless mode.
216    pub loop_controller: ApplicationLoopController<'b>,
217
218    /// Task pool for asynchronous task management.
219    pub task_pool: &'a mut TaskPoolHandler,
220
221    /// A stored state of most common input events. It is used a "shortcut" in cases where event-based
222    /// approach is too verbose. It may be useful in simple scenarios where you just need to know
223    /// if a button (on keyboard, mouse) was pressed and do something.
224    ///
225    /// **Important:** this structure does not track from which device the corresponding event has
226    /// come from, if you have more than one keyboard and/or mouse, use event-based approach instead!
227    pub input_state: &'a InputState,
228}
229
230impl<'a, 'b> PluginContext<'a, 'b> {
231    /// Spawns an asynchronous task that tries to load a user interface from the given path.
232    /// When the task is completed, the specified callback is called that can be used to
233    /// modify the UI. The loaded UI must be registered in the engine, otherwise it will be
234    /// discarded.
235    ///
236    /// ## Example
237    ///
238    /// ```rust
239    /// # use fyrox_impl::{
240    /// #     core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
241    /// #     event::Event,
242    /// #     plugin::{error::GameResult, Plugin, PluginContext, PluginRegistrationContext},
243    /// #     scene::Scene,
244    /// # };
245    /// # use std::str::FromStr;
246    ///
247    /// #[derive(Default, Visit, Reflect, Debug)]
248    /// #[reflect(non_cloneable)]
249    /// struct MyGame {}
250    ///
251    /// impl Plugin for MyGame {
252    ///     fn init(&mut self, _scene_path: Option<&str>, mut ctx: PluginContext) -> GameResult {
253    ///         ctx.load_ui("data/my.ui", |result, game: &mut MyGame, mut ctx| {
254    ///             // The loaded UI must be registered in the engine.
255    ///             ctx.user_interfaces.add(result?.payload);
256    ///             Ok(())
257    ///         });
258    ///         Ok(())
259    ///     }
260    /// }
261    /// ```
262    pub fn load_ui<U, P, C>(&mut self, path: U, callback: C)
263    where
264        U: Into<PathBuf>,
265        P: Plugin,
266        for<'c, 'd> C:
267            FnOnce(UiLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
268    {
269        let path = path.into();
270        self.task_pool.spawn_plugin_task(
271            UserInterface::load_from_file(
272                path.clone(),
273                self.widget_constructors.clone(),
274                self.dyn_type_constructors.clone(),
275                self.resource_manager.clone(),
276            ),
277            move |result, plugin, ctx| match result {
278                Ok((ui, data)) => callback(
279                    Ok(LoaderOutput {
280                        payload: ui,
281                        data,
282                        path,
283                    }),
284                    plugin,
285                    ctx,
286                ),
287                Err(e) => callback(Err(e), plugin, ctx),
288            },
289        );
290    }
291
292    /// Tries to load a game scene at the given path.
293    ///
294    /// This method has a special flag `is_derived` which dictates how to load the scene:
295    ///
296    /// - `false` - the scene is loaded as-is and returned to the caller. Use this option if you
297    ///   don't want to use built-in "saved game" system. See the next option for details.
298    /// - `true`, then the requested scene is loaded and a new model resource is
299    ///   registered in the resource manager that references the scene source file. Then the loaded
300    ///   scene is _cloned_ and all its nodes links to their originals in the source file. Then this
301    ///   cloned and processed scene ("derived") is returned. This process essentially links the
302    ///   scene to its source file, so when the derived scene is saved to disk, it does not save all
303    ///   its content, but only changes from the original scene. Derived scenes are used to create
304    ///   saved games. Keep in mind, if you've created a derived scene and saved it, you must load
305    ///   this saved game with `is_derived` set to `false`.
306    ///
307    /// # Example
308    ///
309    /// ```rust
310    /// # use fyrox_impl::{
311    /// #     core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
312    /// #     event::Event,
313    /// #     plugin::{error::GameResult, SceneLoaderResult, Plugin, PluginContext, PluginRegistrationContext},
314    /// #     scene::Scene,
315    /// # };
316    /// # use std::str::FromStr;
317    /// #
318    /// #[derive(Default, Visit, Reflect, Debug)]
319    /// #[reflect(non_cloneable)]
320    /// struct MyGame {}
321    ///
322    /// impl MyGame {
323    ///     fn on_scene_loading_result(&mut self, result: SceneLoaderResult, ctx: &mut PluginContext) -> GameResult {
324    ///         // Register the scene.
325    ///         ctx.scenes.add(result?.payload);
326    ///         Ok(())
327    ///     }
328    /// }
329    ///
330    /// impl Plugin for MyGame {
331    ///     fn init(&mut self, scene_path: Option<&str>, mut ctx: PluginContext) -> GameResult {
332    ///         ctx.load_scene(
333    ///             scene_path.unwrap_or("data/scene.rgs"),
334    ///             false, // See the docs for details.
335    ///             |result, game: &mut MyGame, ctx| game.on_scene_loading_result(result, ctx),
336    ///         );
337    ///         Ok(())
338    ///     }
339    /// }
340    /// ```
341    pub fn load_scene<U, P, C>(&mut self, path: U, is_derived: bool, callback: C)
342    where
343        U: Into<PathBuf>,
344        P: Plugin,
345        for<'c, 'd> C:
346            FnOnce(SceneLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
347    {
348        let path = path.into();
349
350        let serialization_context = self.serialization_context.clone();
351        let dyn_type_constructors = self.dyn_type_constructors.clone();
352        let resource_manager = self.resource_manager.clone();
353        let uuid = resource_manager.find::<Model>(&path).resource_uuid();
354        let io = resource_manager.resource_io();
355
356        self.task_pool.spawn_plugin_task(
357            {
358                let path = path.clone();
359                async move {
360                    match SceneLoader::from_file(
361                        path,
362                        io.as_ref(),
363                        serialization_context,
364                        dyn_type_constructors,
365                        resource_manager.clone(),
366                    )
367                    .await
368                    {
369                        Ok((loader, data)) => Ok((loader.finish().await, data)),
370                        Err(e) => Err(e),
371                    }
372                }
373            },
374            move |result, plugin, ctx| {
375                match result {
376                    Ok((mut scene, data)) => {
377                        if is_derived {
378                            let model = ctx.resource_manager.find_uuid::<Model>(uuid);
379                            // Create a resource, that will point to the scene we've loaded the
380                            // scene from and force scene nodes to inherit data from them.
381                            let data = Model {
382                                mapping: NodeMapping::UseHandles,
383                                // We have to create a full copy of the scene, because otherwise
384                                // some methods (`Base::root_resource` in particular) won't work
385                                // correctly.
386                                scene: scene.clone_one_to_one().0,
387                            };
388                            model.header().state.commit_ok(data);
389
390                            for (handle, node) in scene.graph.pair_iter_mut() {
391                                node.set_inheritance_data(handle, model.clone());
392                            }
393
394                            // Reset modified flags in every inheritable property of the scene.
395                            // Except nodes, they're inherited in a separate place.
396                            (&mut scene as &mut dyn Reflect).apply_recursively_mut(
397                                &mut |object| {
398                                    let type_id = (*object).type_id();
399                                    if type_id != TypeId::of::<NodePool>() {
400                                        object.as_inheritable_variable_mut(&mut |variable| {
401                                            if let Some(variable) = variable {
402                                                variable.reset_modified_flag();
403                                            }
404                                        });
405                                    }
406                                },
407                                &[
408                                    TypeId::of::<UntypedResource>(),
409                                    TypeId::of::<navmesh::Container>(),
410                                ],
411                            )
412                        } else {
413                            // Take scene data from the source scene.
414                            if let Some(source_asset) =
415                                scene.graph[scene.graph.get_root()].root_resource()
416                            {
417                                let source_asset_ref = source_asset.data_ref();
418                                let source_scene_ref = &source_asset_ref.scene;
419                                Log::verify(try_inherit_properties(
420                                    &mut scene,
421                                    source_scene_ref,
422                                    &[
423                                        TypeId::of::<NodePool>(),
424                                        TypeId::of::<UntypedResource>(),
425                                        TypeId::of::<navmesh::Container>(),
426                                    ],
427                                ));
428                            }
429                        }
430
431                        callback(
432                            Ok(LoaderOutput {
433                                payload: scene,
434                                path,
435                                data,
436                            }),
437                            plugin,
438                            ctx,
439                        )
440                    }
441                    Err(error) => callback(Err(error), plugin, ctx),
442                }
443            },
444        );
445    }
446
447    /// Tries to load either a game scene (via [`Self::load_scene`] or a user interface [`Self::load_ui`].
448    /// This method tries to guess the actual type of the asset by comparing the extension in the
449    /// path. When a game scene or a UI is loaded, it is added to the respective engine container.
450    pub fn load_scene_or_ui<P: Plugin>(&mut self, path: impl AsRef<Path>) {
451        let path = path.as_ref();
452        let ext = path
453            .extension()
454            .map(|ext| ext.to_string_lossy().to_string())
455            .unwrap_or_default();
456        match ext.as_str() {
457            "rgs" => self.load_scene(path, false, |result, _: &mut P, ctx| {
458                ctx.scenes.add(result?.payload);
459                Ok(())
460            }),
461            "ui" => self.load_ui(path, |result, _: &mut P, ctx| {
462                ctx.user_interfaces.add(result?.payload);
463                Ok(())
464            }),
465            _ => err!("File {path:?} is not a game scene nor a user interface!"),
466        }
467    }
468}
469
470define_as_any_trait!(PluginAsAny => Plugin);
471
472impl dyn Plugin {
473    /// Performs downcasting to a particular type.
474    pub fn cast<T: Plugin>(&self) -> Option<&T> {
475        PluginAsAny::as_any(self).downcast_ref::<T>()
476    }
477
478    /// Performs downcasting to a particular type.
479    pub fn cast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
480        PluginAsAny::as_any_mut(self).downcast_mut::<T>()
481    }
482}
483
484/// Plugin is a convenient interface that allow you to extend engine's functionality.
485///
486/// # Example
487///
488/// ```rust
489/// # use fyrox_impl::{
490/// #     core::{pool::Handle}, core::visitor::prelude::*, core::reflect::prelude::*,
491/// #     plugin::{Plugin, PluginContext, PluginRegistrationContext, error::GameResult},
492/// #     scene::Scene,
493/// #     event::Event
494/// # };
495/// # use std::str::FromStr;
496///
497/// #[derive(Default, Visit, Reflect, Debug)]
498/// #[reflect(non_cloneable)]
499/// struct MyPlugin {}
500///
501/// impl Plugin for MyPlugin {
502///     fn on_deinit(&mut self, context: PluginContext) -> GameResult {
503///         // The method is called when the plugin is disabling.
504///         // The implementation is optional.
505///         Ok(())
506///     }
507///
508///     fn update(&mut self, context: &mut PluginContext) -> GameResult {
509///         // The method is called on every frame, it is guaranteed to have fixed update rate.
510///         // The implementation is optional.
511///         Ok(())
512///     }
513///
514///     fn on_os_event(&mut self, event: &Event<()>, context: PluginContext) -> GameResult {
515///         // The method is called when the main window receives an event from the OS.
516///         Ok(())
517///     }
518/// }
519/// ```
520///
521/// # Error Handling
522///
523/// Every plugin method returns [`GameResult`] (which is a simple wrapper over `Result<(), GameError>`),
524/// this helps to reduce the amount of boilerplate code related to error handling. There are a number
525/// of errors that can be automatically handled via `?` operator. All supported error types listed
526/// in [`error::GameError`] enum.
527///
528/// The following code snippet shows the most common use cases for error handling:
529///
530/// ```rust
531/// # use fyrox_impl::{
532/// #     core::{err, pool::Handle, reflect::prelude::*, visitor::prelude::*},
533/// #     event::Event,
534/// #     graph::SceneGraph,
535/// #     plugin::{error::GameResult, Plugin, PluginContext, PluginRegistrationContext},
536/// #     scene::{node::Node, Scene},
537/// # };
538/// # use std::str::FromStr;
539/// #[derive(Default, Visit, Reflect, Debug)]
540/// #[reflect(non_cloneable)]
541/// struct MyPlugin {
542///     scene: Handle<Scene>,
543///     player: Handle<Node>,
544/// }
545///
546/// impl Plugin for MyPlugin {
547///     fn update(&mut self, context: &mut PluginContext) -> GameResult {
548///         // 1. This is the old approach.
549///         match context.scenes.try_get(self.scene) {
550///             Ok(scene) => match scene.graph.try_get(self.player) {
551///                 Ok(player) => {
552///                     println!("Player name is: {}", player.name());
553///                 }
554///                 Err(error) => {
555///                     err!("Unable to borrow the player. Reason: {error}")
556///                 }
557///             },
558///             Err(error) => {
559///                 err!("Unable to borrow the scene. Reason: {error}")
560///             }
561///         }
562///
563///         // 2. This is the same code as above, but with shortcuts for easier error handling.
564///         // Message report is will be something like this:
565///         // `An error occurred during update plugin method call. Reason: <error message>`.
566///         let scene = context.scenes.try_get(self.scene)?;
567///         let player = scene.graph.try_get(self.player)?;
568///         println!("Player name is: {}", player.name());
569///
570///         Ok(())
571///     }
572/// }
573/// ```
574pub trait Plugin: PluginAsAny + Visit + Reflect {
575    /// The method is called when the plugin constructor was just registered in the engine. The main
576    /// use of this method is to register scripts and custom scene graph nodes in [`SerializationContext`].
577    fn register(
578        &self,
579        #[allow(unused_variables)] context: PluginRegistrationContext,
580    ) -> GameResult {
581        Ok(())
582    }
583
584    /// This method is used to register property editors for your game types; to make them editable
585    /// in the editor.
586    fn register_property_editors(
587        &self,
588        #[allow(unused_variables)] editors: Arc<PropertyEditorDefinitionContainer>,
589    ) {
590    }
591
592    /// This method is used to initialize your plugin.
593    fn init(
594        &mut self,
595        #[allow(unused_variables)] scene_path: Option<&str>,
596        #[allow(unused_variables)] context: PluginContext,
597    ) -> GameResult {
598        Ok(())
599    }
600
601    /// This method is called when your plugin was re-loaded from a dynamic library. It could be used
602    /// to restore some runtime state, that cannot be serialized. This method is called **only for
603    /// dynamic plugins!** It is guaranteed to be called after all plugins were constructed, so the
604    /// cross-plugins interactions are possible.
605    fn on_loaded(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
606        Ok(())
607    }
608
609    /// The method is called before plugin will be disabled. It should be used for clean up, or some
610    /// additional actions.
611    fn on_deinit(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
612        Ok(())
613    }
614
615    /// Updates the plugin internals at fixed rate (see [`PluginContext::dt`] parameter for more
616    /// info).
617    fn update(&mut self, #[allow(unused_variables)] context: &mut PluginContext) -> GameResult {
618        Ok(())
619    }
620
621    /// called after all Plugin and Script updates
622    fn post_update(
623        &mut self,
624        #[allow(unused_variables)] context: &mut PluginContext,
625    ) -> GameResult {
626        Ok(())
627    }
628
629    /// The method is called when the main window receives an event from the OS. The main use of
630    /// the method is to respond to some external events, for example an event from keyboard or
631    /// gamepad. See [`Event`] docs for more info.
632    fn on_os_event(
633        &mut self,
634        #[allow(unused_variables)] event: &Event<()>,
635        #[allow(unused_variables)] context: PluginContext,
636    ) -> GameResult {
637        Ok(())
638    }
639
640    /// The method is called when a graphics context was successfully created. It could be useful
641    /// to catch the moment when it was just created and do something in response.
642    fn on_graphics_context_initialized(
643        &mut self,
644        #[allow(unused_variables)] context: PluginContext,
645    ) -> GameResult {
646        Ok(())
647    }
648
649    /// The method is called before the actual frame rendering. It could be useful to render off-screen
650    /// data (render something to texture, that can be used later in the main frame).
651    fn before_rendering(
652        &mut self,
653        #[allow(unused_variables)] context: PluginContext,
654    ) -> GameResult {
655        Ok(())
656    }
657
658    /// The method is called when the current graphics context was destroyed.
659    fn on_graphics_context_destroyed(
660        &mut self,
661        #[allow(unused_variables)] context: PluginContext,
662    ) -> GameResult {
663        Ok(())
664    }
665
666    /// The method will be called when there is any message from a user interface (UI) instance
667    /// of the engine. Use `ui_handle` parameter to find out from which UI the message has come
668    /// from.
669    fn on_ui_message(
670        &mut self,
671        #[allow(unused_variables)] context: &mut PluginContext,
672        #[allow(unused_variables)] message: &UiMessage,
673        #[allow(unused_variables)] ui_handle: Handle<UserInterface>,
674    ) -> GameResult {
675        Ok(())
676    }
677
678    /// This method is called when a game error has occurred, allowing you to perform some
679    /// specific action to react to it (for example - to show an error message UI in your game).
680    ///
681    /// ## Important notes
682    ///
683    /// This method is called at the end of the current frame, and before that, the engine collects
684    /// all the errors into a queue and then processes them one by one. This means that this method
685    /// won't be called immediately when an error was returned by any of your plugin or script methods,
686    /// but instead the processing will be delayed to the end of the frame.
687    ///
688    /// The error passed by a reference here instead of by-value, because there could be multiple
689    /// plugins that can handle the error. This might seem counterintuitive, but remember that
690    /// [`GameError`] can occur during script execution, which is not a part of a plugin and its
691    /// methods executed separately, outside the plugin routines.
692    ///
693    /// ## Error handling
694    ///
695    /// This method should return `true` if the error was handled and no logging is needed, otherwise
696    /// it should return `false` and in this case, the error will be logged by the engine. When
697    /// `true` is returned by the plugin, the error won't be passed to any other plugins. By default,
698    /// this method returns `false`, which means that it does not handle any errors and the engine
699    /// will log the errors as usual.
700    fn on_game_error(
701        &mut self,
702        #[allow(unused_variables)] context: &mut PluginContext,
703        #[allow(unused_variables)] error: &GameError,
704    ) -> bool {
705        false
706    }
707}