diff --git a/fyrox-impl/src/engine/mod.rs b/fyrox-impl/src/engine/mod.rs
index 6bba68271..44ff4dd6a 100644
--- a/fyrox-impl/src/engine/mod.rs
+++ b/fyrox-impl/src/engine/mod.rs
@@ -79,6 +79,7 @@ use raw_window_handle::HasRawWindowHandle;
use std::{ffi::CString, num::NonZeroU32};
use std::ffi::OsStr;
+use std::io::Cursor;
use std::{
any::TypeId,
collections::{HashSet, VecDeque},
@@ -94,6 +95,7 @@ use std::{
use crate::plugin::dynamic::DynamicPlugin;
use crate::plugin::PluginContainer;
+use fyrox_core::visitor::Visitor;
use winit::{
dpi::{Position, Size},
event_loop::EventLoopWindowTarget,
@@ -2298,15 +2300,24 @@ impl Engine {
}
}
+ fn register_plugin(&self, plugin: &dyn Plugin) {
+ *self
+ .serialization_context
+ .script_constructors
+ .context_type_id
+ .lock() = plugin.type_id();
+ plugin.register(PluginRegistrationContext {
+ serialization_context: &self.serialization_context,
+ resource_manager: &self.resource_manager,
+ });
+ }
+
/// Adds a new static plugin.
pub fn add_plugin
(&mut self, plugin: P)
where
P: Plugin + 'static,
{
- plugin.register(PluginRegistrationContext {
- serialization_context: &self.serialization_context,
- resource_manager: &self.resource_manager,
- });
+ self.register_plugin(&plugin);
self.plugins.push(PluginContainer::Static(Box::new(plugin)));
}
@@ -2319,17 +2330,138 @@ impl Engine {
where
P: AsRef + 'static,
{
- let plugin = PluginContainer::Dynamic(DynamicPlugin::load(path)?);
-
- plugin.register(PluginRegistrationContext {
- serialization_context: &self.serialization_context,
- resource_manager: &self.resource_manager,
- });
+ let dynamic = DynamicPlugin::load(path)?;
+ self.register_plugin(dynamic.plugin());
+ let plugin = PluginContainer::Dynamic(dynamic);
self.plugins.push(plugin);
Ok(())
}
+
+ /// Tries to reload all dynamic plugins registered in the engine. This method will try to "reload"
+ /// the scenes that have scripts from dynamic plugins. Reloading here means, that the engine will
+ /// serialize a scene into a binary blob, then destroy the scene, then reload all the plugins and
+ /// finally deserialize the scene from the binary blob.
+ ///
+ /// ## Important notes
+ ///
+ /// This method is highly dangerous and wasn't properly tested, use at your own risk!
+ pub async fn reload_dynamic_plugins(&mut self) {
+ // Search for scenes, that has scripts provided by a dynamic plugin.
+ // TODO: Add scene nodes here too; plugins can provide custom scene node types.
+ let mut scenes = FxHashSet::default();
+ for plugin in self.plugins.iter() {
+ if let PluginContainer::Dynamic(dynamic) = plugin {
+ let plugin_type_id = dynamic.plugin().type_id();
+
+ for (handle, scene) in self.scenes.pair_iter() {
+ 'node_loop: for node in scene.graph.linear_iter() {
+ for script in node.scripts() {
+ let script_id = script.deref().id();
+
+ if let Some(constructor) = self
+ .serialization_context
+ .script_constructors
+ .map()
+ .get(&script_id)
+ {
+ if constructor.source_type_id == plugin_type_id {
+ scenes.insert(handle);
+ break 'node_loop;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Search for script constructors, that belongs to dynamic plugins.
+ let mut constructors = FxHashSet::default();
+ for plugin in self.plugins.iter() {
+ if let PluginContainer::Dynamic(dynamic) = plugin {
+ let plugin_type_id = dynamic.plugin().type_id();
+
+ for (type_uuid, constructor) in
+ self.serialization_context.script_constructors.map().iter()
+ {
+ if constructor.source_type_id == plugin_type_id {
+ constructors.insert(*type_uuid);
+ }
+ }
+ }
+ }
+
+ // Remove script constructors, that belongs to dynamic plugins.
+ for type_uuid in constructors.iter() {
+ self.serialization_context
+ .script_constructors
+ .remove(*type_uuid);
+ }
+
+ // Take the scenes out of the container, serialize and destroy the initial scene.
+ let mut serialized_scenes = FxHashMap::default();
+ for &scene_handle in scenes.iter() {
+ let (ticket, mut scene) = self.scenes.take_reserve(scene_handle);
+ let mut visitor = Visitor::new();
+ scene.save("Scene", &mut visitor).unwrap();
+ let mut binary_blob = Cursor::new(Vec::::new());
+ visitor.save_binary_to_memory(&mut binary_blob).unwrap();
+ serialized_scenes.insert(scene_handle, (ticket, binary_blob.into_inner()));
+ }
+
+ // Unload the plugins.
+ enum TemporaryStorage {
+ StaticPlugin(Box),
+ DynamicPlugin(PathBuf),
+ }
+ let plugins = self
+ .plugins
+ .drain(..)
+ .map(|p| match p {
+ PluginContainer::Static(plugin) => TemporaryStorage::StaticPlugin(plugin),
+ PluginContainer::Dynamic(dynamic) => {
+ // Store the path of the dynamic plugin. The plugin itself will be dropped and
+ // thus unloaded.
+ TemporaryStorage::DynamicPlugin(dynamic.path().to_path_buf())
+ }
+ })
+ .collect::>();
+ for plugin in plugins {
+ match plugin {
+ TemporaryStorage::StaticPlugin(plugin) => {
+ self.plugins.push(PluginContainer::Static(plugin))
+ }
+ TemporaryStorage::DynamicPlugin(path) => {
+ let dynamic = DynamicPlugin::load(path).unwrap();
+
+ // Re-register the plugin. This is needed, because it might contain new script
+ // types (or removed ones too).
+ self.register_plugin(dynamic.plugin());
+
+ self.plugins.push(PluginContainer::Dynamic(dynamic))
+ }
+ }
+ }
+
+ // Deserialize the scenes.
+ for (_, (ticket, binary_blob)) in serialized_scenes {
+ let mut visitor = Visitor::load_from_memory(&binary_blob).unwrap();
+ let loader = SceneLoader::load(
+ "Scene",
+ self.serialization_context.clone(),
+ self.resource_manager.clone(),
+ &mut visitor,
+ None,
+ )
+ .unwrap();
+
+ let scene = loader.finish(&self.resource_manager).await;
+
+ self.scenes.put_back(ticket, scene);
+ }
+ }
}
impl Drop for Engine {
diff --git a/fyrox-impl/src/plugin/dynamic.rs b/fyrox-impl/src/plugin/dynamic.rs
index 7a836c535..f04a7620c 100644
--- a/fyrox-impl/src/plugin/dynamic.rs
+++ b/fyrox-impl/src/plugin/dynamic.rs
@@ -45,4 +45,9 @@ impl DynamicPlugin {
pub fn path(&self) -> &Path {
&self.path
}
+
+ /// Return a reference to the plugin interface of the dynamic plugin.
+ pub fn plugin(&self) -> &dyn Plugin {
+ &*self.plugin
+ }
}
diff --git a/fyrox-impl/src/script/constructor.rs b/fyrox-impl/src/script/constructor.rs
index 5e4ffbcfb..9afb8c27a 100644
--- a/fyrox-impl/src/script/constructor.rs
+++ b/fyrox-impl/src/script/constructor.rs
@@ -8,6 +8,7 @@ use crate::{
},
script::{Script, ScriptTrait},
};
+use std::any::{Any, TypeId};
use std::collections::BTreeMap;
/// Script constructor contains all required data and methods to create script instances
@@ -21,15 +22,27 @@ pub struct ScriptConstructor {
/// Script source path.
pub source_path: String,
+
+ /// A type of the source of the script constructor.
+ pub source_type_id: TypeId,
}
/// A special container that is able to create nodes by their type UUID.
-#[derive(Default)]
pub struct ScriptConstructorContainer {
+ pub(crate) context_type_id: Mutex,
// BTreeMap allows to have sorted list of constructors.
map: Mutex>,
}
+impl Default for ScriptConstructorContainer {
+ fn default() -> Self {
+ Self {
+ context_type_id: Mutex::new(().type_id()),
+ map: Default::default(),
+ }
+ }
+}
+
impl ScriptConstructorContainer {
/// Creates default node constructor container with constructors for built-in engine nodes.
pub fn new() -> Self {
@@ -51,6 +64,7 @@ impl ScriptConstructorContainer {
constructor: Box::new(|| Script::new(T::default())),
name: name.to_owned(),
source_path: T::source_path().to_owned(),
+ source_type_id: *self.context_type_id.lock(),
},
);