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;
26
27use crate::engine::input::InputState;
28use crate::engine::ApplicationLoopController;
29use crate::{
30 asset::manager::ResourceManager,
31 core::{pool::Handle, reflect::Reflect, visitor::Visit},
32 engine::{
33 task::TaskPoolHandler, AsyncSceneLoader, GraphicsContext, PerformanceStatistics,
34 ScriptProcessor, SerializationContext,
35 },
36 event::Event,
37 gui::{
38 constructor::WidgetConstructorContainer,
39 inspector::editors::PropertyEditorDefinitionContainer, message::UiMessage, UiContainer,
40 },
41 scene::{Scene, SceneContainer},
42};
43use fyrox_core::define_as_any_trait;
44use fyrox_core::visitor::error::VisitError;
45use fyrox_ui::UserInterface;
46use std::{
47 ops::{Deref, DerefMut},
48 path::Path,
49 sync::Arc,
50};
51
52/// A wrapper for various plugin types.
53pub enum PluginContainer {
54 /// Statically linked plugin. Such plugins are meant to be used in final builds, to maximize
55 /// performance of the game.
56 Static(Box<dyn Plugin>),
57 /// Dynamically linked plugin. Such plugins are meant to be used in development mode for rapid
58 /// prototyping.
59 Dynamic(Box<dyn DynamicPlugin>),
60}
61
62/// Abstraction over different kind of plugins that can be reloaded on the fly (whatever it mean).
63/// The instance is polled by engine with `is_reload_needed_now()` time to time. if it returns true,
64/// then engine serializes current plugin state, then calls `unload()` and then calls `load()`
65pub trait DynamicPlugin {
66 /// returns human-redable short description of the plugin
67 fn display_name(&self) -> String;
68
69 /// engine polls is time to time to determine if it's time to reload plugin
70 fn is_reload_needed_now(&self) -> bool;
71
72 /// panics if not loaded
73 fn as_loaded_ref(&self) -> &dyn Plugin;
74
75 /// panics if not loaded
76 fn as_loaded_mut(&mut self) -> &mut dyn Plugin;
77
78 /// returns false if something bad happends during `reload`.
79 /// has no much use except prevention of error spamming
80 fn is_loaded(&self) -> bool;
81
82 /// called before saving state and detaching related objects
83 fn prepare_to_reload(&mut self) {}
84
85 /// called after plugin-related objects are detached
86 /// `fill_and_register` callback exposes plugin instance to engine to register constructors and restore the state
87 /// callback approach allows plugins to do some necessary actions right after plugin is registed
88 fn reload(
89 &mut self,
90 fill_and_register: &mut dyn FnMut(&mut dyn Plugin) -> Result<(), String>,
91 ) -> Result<(), String>;
92}
93
94impl Deref for PluginContainer {
95 type Target = dyn Plugin;
96
97 fn deref(&self) -> &Self::Target {
98 match self {
99 PluginContainer::Static(plugin) => &**plugin,
100 PluginContainer::Dynamic(plugin) => plugin.as_loaded_ref(),
101 }
102 }
103}
104
105impl DerefMut for PluginContainer {
106 fn deref_mut(&mut self) -> &mut Self::Target {
107 match self {
108 PluginContainer::Static(plugin) => &mut **plugin,
109 PluginContainer::Dynamic(plugin) => plugin.as_loaded_mut(),
110 }
111 }
112}
113
114/// Contains plugin environment for the registration stage.
115pub struct PluginRegistrationContext<'a> {
116 /// A reference to serialization context of the engine. See [`SerializationContext`] for more
117 /// info.
118 pub serialization_context: &'a Arc<SerializationContext>,
119 /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
120 /// info.
121 pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
122 /// A reference to the resource manager instance of the engine. Could be used to register resource loaders.
123 pub resource_manager: &'a ResourceManager,
124}
125
126/// Contains plugin environment.
127pub struct PluginContext<'a, 'b> {
128 /// A reference to scene container of the engine. You can add new scenes from [`Plugin`] methods
129 /// by using [`SceneContainer::add`].
130 pub scenes: &'a mut SceneContainer,
131
132 /// A reference to the resource manager, it can be used to load various resources and manage
133 /// them. See [`ResourceManager`] docs for more info.
134 pub resource_manager: &'a ResourceManager,
135
136 /// A reference to user interface container of the engine. The engine guarantees that there's
137 /// at least one user interface exists. Use `context.user_interfaces.first()/first_mut()` to
138 /// get a reference to it.
139 pub user_interfaces: &'a mut UiContainer,
140
141 /// A reference to the graphics_context, it contains a reference to the window and the current renderer.
142 /// It could be [`GraphicsContext::Uninitialized`] if your application is suspended (possible only on
143 /// Android; it is safe to call [`GraphicsContext::as_initialized_ref`] or [`GraphicsContext::as_initialized_mut`]
144 /// on every other platform).
145 pub graphics_context: &'a mut GraphicsContext,
146
147 /// The time (in seconds) that passed since last call of a method in which the context was
148 /// passed. It has fixed value that is defined by a caller (in most cases it is `Executor`).
149 pub dt: f32,
150
151 /// A reference to time accumulator, that holds remaining amount of time that should be used
152 /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus
153 /// stabilizes update rate. The main use of this variable, is to be able to reset `lag` when
154 /// you doing some heavy calculations in a your game loop (i.e. loading a new level) so the
155 /// engine won't try to "catch up" with all the time that was spent in heavy calculation.
156 pub lag: &'b mut f32,
157
158 /// A reference to serialization context of the engine. See [`SerializationContext`] for more
159 /// info.
160 pub serialization_context: &'a Arc<SerializationContext>,
161
162 /// A reference to serialization context of the engine. See [`WidgetConstructorContainer`] for more
163 /// info.
164 pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
165
166 /// Performance statistics from the last frame.
167 pub performance_statistics: &'a PerformanceStatistics,
168
169 /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
170 /// this value is **not** guaranteed to match real time. A user can change delta time with
171 /// which the engine "ticks" and this delta time affects elapsed time.
172 pub elapsed_time: f32,
173
174 /// Script processor is used to run script methods in a strict order.
175 pub script_processor: &'a ScriptProcessor,
176
177 /// Asynchronous scene loader. It is used to request scene loading. See [`AsyncSceneLoader`] docs
178 /// for usage example.
179 pub async_scene_loader: &'a mut AsyncSceneLoader,
180
181 /// Special field that associates the main application event loop (not game loop) with OS-specific
182 /// windows. It also can be used to alternate control flow of the application. `None` if the
183 /// engine is running in headless mode.
184 pub loop_controller: ApplicationLoopController<'b>,
185
186 /// Task pool for asynchronous task management.
187 pub task_pool: &'a mut TaskPoolHandler,
188
189 /// A stored state of most common input events. It is used a "shortcut" in cases where event-based
190 /// approach is too verbose. It may be useful in simple scenarios where you just need to know
191 /// if a button (on keyboard, mouse) was pressed and do something.
192 ///
193 /// **Important:** this structure does not track from which device the corresponding event has
194 /// come from, if you have more than one keyboard and/or mouse, use event-based approach instead!
195 pub input_state: &'a InputState,
196}
197
198define_as_any_trait!(PluginAsAny => Plugin);
199
200impl dyn Plugin {
201 /// Performs downcasting to a particular type.
202 pub fn cast<T: Plugin>(&self) -> Option<&T> {
203 PluginAsAny::as_any(self).downcast_ref::<T>()
204 }
205
206 /// Performs downcasting to a particular type.
207 pub fn cast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
208 PluginAsAny::as_any_mut(self).downcast_mut::<T>()
209 }
210}
211
212/// Plugin is a convenient interface that allow you to extend engine's functionality.
213///
214/// # Static vs dynamic plugins
215///
216/// Every plugin must be linked statically to ensure that everything is memory safe. There was some
217/// long research about hot reloading and dynamic plugins (in DLLs) and it turned out that they're
218/// not guaranteed to be memory safe because Rust does not have stable ABI. When a plugin compiled
219/// into DLL, Rust compiler is free to reorder struct members in any way it needs to. It is not
220/// guaranteed that two projects that uses the same library will have compatible ABI. This fact
221/// indicates that you either have to use static linking of your plugins or provide C interface
222/// to every part of the engine and "communicate" with plugin using C interface with C ABI (which
223/// is standardized and guaranteed to be compatible). The main problem with C interface is
224/// boilerplate code and the need to mark every structure "visible" through C interface with
225/// `#[repr(C)]` attribute which is not always easy and even possible (because some structures could
226/// be re-exported from dependencies). These are the main reasons why the engine uses static plugins.
227///
228/// # Example
229///
230/// ```rust
231/// # use fyrox_impl::{
232/// # core::{pool::Handle}, core::visitor::prelude::*, core::reflect::prelude::*,
233/// # plugin::{Plugin, PluginContext, PluginRegistrationContext},
234/// # scene::Scene,
235/// # event::Event
236/// # };
237/// # use std::str::FromStr;
238///
239/// #[derive(Default, Visit, Reflect, Debug)]
240/// #[reflect(non_cloneable)]
241/// struct MyPlugin {}
242///
243/// impl Plugin for MyPlugin {
244/// fn on_deinit(&mut self, context: PluginContext) {
245/// // The method is called when the plugin is disabling.
246/// // The implementation is optional.
247/// }
248///
249/// fn update(&mut self, context: &mut PluginContext) {
250/// // The method is called on every frame, it is guaranteed to have fixed update rate.
251/// // The implementation is optional.
252/// }
253///
254/// fn on_os_event(&mut self, event: &Event<()>, context: PluginContext) {
255/// // The method is called when the main window receives an event from the OS.
256/// }
257/// }
258/// ```
259pub trait Plugin: PluginAsAny + Visit + Reflect {
260 /// The method is called when the plugin constructor was just registered in the engine. The main
261 /// use of this method is to register scripts and custom scene graph nodes in [`SerializationContext`].
262 fn register(&self, #[allow(unused_variables)] context: PluginRegistrationContext) {}
263
264 /// This method is used to register property editors for your game types; to make them editable
265 /// in the editor.
266 fn register_property_editors(&self) -> PropertyEditorDefinitionContainer {
267 PropertyEditorDefinitionContainer::empty()
268 }
269
270 /// This method is used to initialize your plugin.
271 fn init(
272 &mut self,
273 #[allow(unused_variables)] scene_path: Option<&str>,
274 #[allow(unused_variables)] context: PluginContext,
275 ) {
276 }
277
278 /// This method is called when your plugin was re-loaded from a dynamic library. It could be used
279 /// to restore some runtime state, that cannot be serialized. This method is called **only for
280 /// dynamic plugins!** It is guaranteed to be called after all plugins were constructed, so the
281 /// cross-plugins interactions are possible.
282 fn on_loaded(&mut self, #[allow(unused_variables)] context: PluginContext) {}
283
284 /// The method is called before plugin will be disabled. It should be used for clean up, or some
285 /// additional actions.
286 fn on_deinit(&mut self, #[allow(unused_variables)] context: PluginContext) {}
287
288 /// Updates the plugin internals at fixed rate (see [`PluginContext::dt`] parameter for more
289 /// info).
290 fn update(&mut self, #[allow(unused_variables)] context: &mut PluginContext) {}
291
292 /// called after all Plugin and Script updates
293 fn post_update(&mut self, #[allow(unused_variables)] context: &mut PluginContext) {}
294
295 /// The method is called when the main window receives an event from the OS. The main use of
296 /// the method is to respond to some external events, for example an event from keyboard or
297 /// gamepad. See [`Event`] docs for more info.
298 fn on_os_event(
299 &mut self,
300 #[allow(unused_variables)] event: &Event<()>,
301 #[allow(unused_variables)] context: PluginContext,
302 ) {
303 }
304
305 /// The method is called when a graphics context was successfully created. It could be useful
306 /// to catch the moment when it was just created and do something in response.
307 fn on_graphics_context_initialized(
308 &mut self,
309 #[allow(unused_variables)] context: PluginContext,
310 ) {
311 }
312
313 /// The method is called before the actual frame rendering. It could be useful to render off-screen
314 /// data (render something to texture, that can be used later in the main frame).
315 fn before_rendering(&mut self, #[allow(unused_variables)] context: PluginContext) {}
316
317 /// The method is called when the current graphics context was destroyed.
318 fn on_graphics_context_destroyed(&mut self, #[allow(unused_variables)] context: PluginContext) {
319 }
320
321 /// The method will be called when there is any message from a user interface (UI) instance
322 /// of the engine. Use `ui_handle` parameter to find out from which UI the message has come
323 /// from.
324 fn on_ui_message(
325 &mut self,
326 #[allow(unused_variables)] context: &mut PluginContext,
327 #[allow(unused_variables)] message: &UiMessage,
328 #[allow(unused_variables)] ui_handle: Handle<UserInterface>,
329 ) {
330 }
331
332 /// This method is called when the engine starts loading a scene from the given `path`. It could
333 /// be used to "catch" the moment when the scene is about to be loaded; to show a progress bar
334 /// for example. See [`AsyncSceneLoader`] docs for usage example.
335 fn on_scene_begin_loading(
336 &mut self,
337 #[allow(unused_variables)] path: &Path,
338 #[allow(unused_variables)] context: &mut PluginContext,
339 ) {
340 }
341
342 /// This method is called when the engine finishes loading a scene from the given `path`. Use
343 /// this method if you need do something with a newly loaded scene. See [`AsyncSceneLoader`] docs
344 /// for usage example.
345 fn on_scene_loaded(
346 &mut self,
347 #[allow(unused_variables)] path: &Path,
348 #[allow(unused_variables)] scene: Handle<Scene>,
349 #[allow(unused_variables)] data: &[u8],
350 #[allow(unused_variables)] context: &mut PluginContext,
351 ) {
352 }
353
354 /// This method is called when the engine finishes loading a scene from the given `path` with
355 /// some error. This method could be used to report any issues to a user.
356 fn on_scene_loading_failed(
357 &mut self,
358 #[allow(unused_variables)] path: &Path,
359 #[allow(unused_variables)] error: &VisitError,
360 #[allow(unused_variables)] context: &mut PluginContext,
361 ) {
362 }
363}