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

#![warn(missing_docs)]

use crate::{
    core::pool::Handle,
    core::uuid::Uuid,
    engine::{resource_manager::ResourceManager, SerializationContext},
    event::Event,
    renderer::Renderer,
    scene::{Scene, SceneContainer},
};
use std::sync::Arc;

/// Contains plugin environment for the registration stage.
pub struct PluginRegistrationContext {
    /// A reference to serialization context of the engine. See [`SerializationContext`] for more
    /// info.
    pub serialization_context: Arc<SerializationContext>,
}

/// Contains plugin environment.
pub struct PluginContext<'a> {
    /// `true` if  the plugin running under the editor, `false` - otherwise.
    pub is_in_editor: bool,

    /// A reference to scene container of the engine. You can add new scenes from [`Plugin`] methods
    /// by using [`SceneContainer::add`].
    ///
    /// # Important notes
    ///
    /// Do not clear this container when running your plugin in the editor, otherwise you'll get
    /// panic. Every scene that was added in the container while "play mode" in the editor was
    /// active will be removed when you leave play mode.
    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 the renderer, it can be used to add custom render passes (for example to
    /// render custom effects and so on).
    pub renderer: &'a mut Renderer,

    /// The time (in seconds) that passed since last call of a method in which the context was
    /// passed.
    pub dt: f32,

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

/// Plugin is a convenient interface that allow you to extend engine's functionality.
///
/// # Engine vs Framework
///
/// There are two completely different approaches that could be used to use the engine: you either
/// use the engine in "true" engine mode, or use it in framework mode. The "true" engine mode fixes
/// high-level structure of your game and forces you to implement game logic inside plugins and
/// scripts. The framework mode provides low-level access to engine details and leaves implementation
/// details to you.
///
/// By default the engine, if used alone, **completely ignores** every plugin, it calls a few methods
/// ([`Plugin::on_register`], [`Plugin::on_standalone_init`]) and does not call any other methods.
/// The plugins are meant to be used only in "true" engine mode. If you're using the engine alone
/// (without the editor, executor, and required project structure), it means that you're using the
/// engine in **framework** mode and you're able to setup your project as you want.
///
/// The plugins managed either by `Executor` or the editor (`Fyroxed`). The first one is a small
/// framework that calls all methods of the plugin as it needs to be, `Executor` is used to build
/// final binary of your game. The editor is also able to use plugins, it manages them in special
/// way that guarantees some invariants.
///
/// # Interface details
///
/// There is one confusing part in the plugin interface: two methods that looks like they're doing
/// the same thing - [`Plugin::on_standalone_init`] and [`Plugin::on_enter_play_mode`]. However
/// there is one major difference in the two. The first method is called when the plugin is running
/// in a standalone mode (in game executor, which is final binary of your game). The second is used
/// in the editor and called when the editor enters "play mode".
///
/// The "play mode" is special and should be described a bit more. The editor is able to edit
/// scenes, there could be only one scene opened at a time. However your game could use multiple
/// scenes (for example one for game menu and one per game level). This fact leads to a problem:
/// how the game will know which scene is currently edited and requested for "play mode"?
/// [`Plugin::on_enter_play_mode`] solves the problem by providing you the handle to the active
/// scene, in this method you should force your game to use provided scene.
///
/// # 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::{
///     core::{pool::Handle, uuid::{uuid,Uuid}},
///     plugin::{Plugin, PluginContext, PluginRegistrationContext},
///     scene::Scene,
///     event::Event
/// };
/// use std::str::FromStr;
///
/// struct MyPlugin {}
///
/// impl Plugin for MyPlugin {
///     fn on_register(&mut self, context: PluginRegistrationContext) {
///         // The method is called when the plugin was just registered in the engine.
///         // Register your scripts here using `context`.
///         // The implementation is optional.
///     }
///
///     fn on_standalone_init(&mut self, context: PluginContext) {
///         // The method is called when the plugin is running in standalone mode (editor-less).
///         // The implementation is optional.
///     }
///
///     fn on_enter_play_mode(&mut self, scene: Handle<Scene>, context: PluginContext) {
///         // The method is called when the plugin is running inside the editor and it enters
///         // "play mode".
///         // The implementation is optional.
///     }
///
///     fn on_leave_play_mode(&mut self, context: PluginContext) {
///         // The method is called when the plugin is running inside the editor and it leaves
///         // "play mode".
///         // The implementation is optional.
///     }
///
///     fn on_unload(&mut self, context: &mut PluginContext) {
///         // The method is called when the game/editor is about to shutdown.
///         // 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 id(&self) -> Uuid {
///         // The method must return persistent type id.
///         // Use https://www.uuidgenerator.net/ to generate one.
///         uuid!("b9302812-81a7-48a5-89d2-921774d94943")
///     }
///
///     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: 'static {
    /// The method is called when the plugin was just registered in the engine. The main use of the
    /// method is to register scripts and custom scene graph nodes in [`SerializationContext`].
    fn on_register(&mut self, #[allow(unused_variables)] context: PluginRegistrationContext) {}

    /// The method is called when the plugin is registered in game executor. It is guaranteed to be
    /// called once.
    ///
    /// # Important notes
    ///
    /// The method is **not** called if the plugin is running in the editor! Use
    /// [`Self::on_enter_play_mode`] instead.
    fn on_standalone_init(&mut self, #[allow(unused_variables)] context: PluginContext) {}

    /// The method is called if the plugin running in the editor and the editor enters play mode.
    ///
    /// # Important notes
    ///
    /// The method replaces [`Self::on_standalone_init`] when the plugin runs in the editor! Use
    /// the method to obtain a handle to the scene being edited in the editor.
    fn on_enter_play_mode(
        &mut self,
        #[allow(unused_variables)] scene: Handle<Scene>,
        #[allow(unused_variables)] context: PluginContext,
    ) {
    }

    /// The method is called when the plugin is running inside the editor and it leaves
    /// "play mode".
    fn on_leave_play_mode(&mut self, #[allow(unused_variables)] context: PluginContext) {}

    /// The method is called when the game/editor is about to shutdown.
    fn on_unload(&mut self, #[allow(unused_variables)] context: &mut 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 must return persistent type id. The id is used for serialization, the engine
    /// saves the id into file (scene in most cases) and when you loading file it re-creates
    /// correct plugin using the id.
    ///
    /// # Important notes
    ///
    /// Do **not** use [`Uuid::new_v4`] or any other [`Uuid`] methods that generates ids, ids
    /// generated using these methods are **random** and are not suitable for serialization!
    ///
    /// # How to obtain UUID
    ///
    /// Use <https://www.uuidgenerator.net/> to generate one.
    fn id(&self) -> Uuid;

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