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
//! Everything related to plugins. See [`Plugin`] docs for more info.

#![warn(missing_docs)]

pub mod dynamic;

use crate::{
    asset::manager::ResourceManager,
    core::{
        notify::RecommendedWatcher, pool::Handle, reflect::Reflect, visitor::Visit,
        visitor::VisitError,
    },
    engine::{
        task::TaskPoolHandler, AsyncSceneLoader, GraphicsContext, PerformanceStatistics,
        ScriptProcessor, SerializationContext,
    },
    event::Event,
    gui::{
        constructor::WidgetConstructorContainer,
        inspector::editors::PropertyEditorDefinitionContainer, message::UiMessage, UiContainer,
    },
    plugin::dynamic::DynamicPlugin,
    scene::{Scene, SceneContainer},
};
use std::{
    any::Any,
    ops::{Deref, DerefMut},
    path::{Path, PathBuf},
    sync::{atomic::AtomicBool, Arc},
};
use winit::event_loop::EventLoopWindowTarget;

/// Actual state of a dynamic plugin.
pub enum DynamicPluginState {
    /// Unloaded plugin.
    Unloaded {
        /// Serialized content of the plugin.
        binary_blob: Vec<u8>,
    },
    /// Loaded plugin.
    Loaded(DynamicPlugin),
}

impl DynamicPluginState {
    /// Tries to interpret the state as [`Self::Loaded`], panics if the plugin is unloaded.
    pub fn as_loaded_ref(&self) -> &DynamicPlugin {
        match self {
            DynamicPluginState::Unloaded { .. } => {
                panic!("Cannot obtain a reference to the plugin, because it is unloaded!")
            }
            DynamicPluginState::Loaded(dynamic) => dynamic,
        }
    }

    /// Tries to interpret the state as [`Self::Loaded`], panics if the plugin is unloaded.
    pub fn as_loaded_mut(&mut self) -> &mut DynamicPlugin {
        match self {
            DynamicPluginState::Unloaded { .. } => {
                panic!("Cannot obtain a reference to the plugin, because it is unloaded!")
            }
            DynamicPluginState::Loaded(dynamic) => dynamic,
        }
    }
}

/// 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 {
        /// Dynamic plugin state.
        state: DynamicPluginState,
        /// Target path of the library of the plugin.
        lib_path: PathBuf,
        /// Path to the source file, that is emitted by the compiler. If hot reloading is enabled,
        /// this library will be cloned to `lib_path` and loaded. This is needed, because usually
        /// OS locks the library and it is not possible to overwrite it while it is loaded in a process.  
        source_lib_path: PathBuf,
        /// Optional file system watcher, that is configured to watch the source library and re-load
        /// the plugin if the source library has changed. If the watcher is `None`, then hot reloading
        /// is disabled.
        watcher: Option<RecommendedWatcher>,
        /// A flag, that tells the engine that the plugin needs to be reloaded. Usually the engine
        /// will do that at the end of the update tick.
        need_reload: Arc<AtomicBool>,
    },
}

impl Deref for PluginContainer {
    type Target = dyn Plugin;

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

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

/// 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 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>,

    /// 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,

    /// Asynchronous scene loader. It is used to request scene loading. See [`AsyncSceneLoader`] docs
    /// for usage example.
    pub async_scene_loader: &'a mut AsyncSceneLoader,

    /// Special field that associates main application event loop (not game loop) with OS-specific
    /// windows. It also can be used to alternate control flow of the application.
    pub window_target: Option<&'b EventLoopWindowTarget<()>>,

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

/// Base plugin automatically implements type casting for plugins.
pub trait BasePlugin: Any + 'static {
    /// Returns a reference to Any trait. It is used for type casting.
    fn as_any(&self) -> &dyn Any;

    /// Returns a reference to Any trait. It is used for type casting.
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

impl<T> BasePlugin for T
where
    T: Any + Plugin + 'static,
{
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

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

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

/// Plugin is a convenient interface that allow you to extend engine's functionality.
///
/// # Static vs dynamic plugins
///
/// Every plugin must be linked statically to ensure that everything is memory safe. There was some
/// long research about hot reloading and dynamic plugins (in DLLs) and it turned out that they're
/// not guaranteed to be memory safe because Rust does not have stable ABI. When a plugin compiled
/// into DLL, Rust compiler is free to reorder struct members in any way it needs to. It is not
/// guaranteed that two projects that uses the same library will have compatible ABI. This fact
/// indicates that you either have to use static linking of your plugins or provide C interface
/// to every part of the engine and "communicate" with plugin using C interface with C ABI (which
/// is standardized and guaranteed to be compatible). The main problem with C interface is
/// boilerplate code and the need to mark every structure "visible" through C interface with
/// `#[repr(C)]` attribute which is not always easy and even possible (because some structures could
/// be re-exported from dependencies). These are the main reasons why the engine uses static plugins.
///
/// # Example
///
/// ```rust
/// # use fyrox_impl::{
/// #     core::{pool::Handle}, core::visitor::prelude::*, core::reflect::prelude::*,
/// #     plugin::{Plugin, PluginContext, PluginRegistrationContext},
/// #     scene::Scene,
/// #     event::Event
/// # };
/// # use std::str::FromStr;
///
/// #[derive(Default, Visit, Reflect, Debug)]
/// struct MyPlugin {}
///
/// impl Plugin for MyPlugin {
///     fn on_deinit(&mut self, context: PluginContext) {
///         // The method is called when the plugin is disabling.
///         // The implementation is optional.
///     }
///
///     fn update(&mut self, context: &mut PluginContext) {
///         // The method is called on every frame, it is guaranteed to have fixed update rate.
///         // The implementation is optional.
///     }
///
///     fn on_os_event(&mut self, event: &Event<()>, context: PluginContext) {
///         // The method is called when the main window receives an event from the OS.
///     }
/// }
/// ```
pub trait Plugin: BasePlugin + 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) {}

    /// This method is used to register property editors for your game types; to make them editable
    /// in the editor.
    fn register_property_editors(&self) -> PropertyEditorDefinitionContainer {
        PropertyEditorDefinitionContainer::empty()
    }

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

    /// 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) {}

    /// 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) {}

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

    /// 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,
    ) {
    }

    /// 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,
    ) {
    }

    /// 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) {}

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

    /// The method will be called when there is any message from main user interface instance
    /// of the engine.
    fn on_ui_message(
        &mut self,
        #[allow(unused_variables)] context: &mut PluginContext,
        #[allow(unused_variables)] message: &UiMessage,
    ) {
    }

    /// This method is called when the engine starts loading a scene from the given `path`. It could
    /// be used to "catch" the moment when the scene is about to be loaded; to show a progress bar
    /// for example. See [`AsyncSceneLoader`] docs for usage example.
    fn on_scene_begin_loading(
        &mut self,
        #[allow(unused_variables)] path: &Path,
        #[allow(unused_variables)] context: &mut PluginContext,
    ) {
    }

    /// This method is called when the engine finishes loading a scene from the given `path`. Use
    /// this method if you need do something with a newly loaded scene. See [`AsyncSceneLoader`] docs
    /// for usage example.
    fn on_scene_loaded(
        &mut self,
        #[allow(unused_variables)] path: &Path,
        #[allow(unused_variables)] scene: Handle<Scene>,
        #[allow(unused_variables)] data: &[u8],
        #[allow(unused_variables)] context: &mut PluginContext,
    ) {
    }

    /// This method is called when the engine finishes loading a scene from the given `path` with
    /// some error. This method could be used to report any issues to a user.
    fn on_scene_loading_failed(
        &mut self,
        #[allow(unused_variables)] path: &Path,
        #[allow(unused_variables)] error: &VisitError,
        #[allow(unused_variables)] context: &mut PluginContext,
    ) {
    }
}