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}