diff --git a/fyrox-impl/src/engine/hotreload.rs b/fyrox-impl/src/engine/hotreload.rs new file mode 100644 index 000000000..f1a2bfc57 --- /dev/null +++ b/fyrox-impl/src/engine/hotreload.rs @@ -0,0 +1,221 @@ +use crate::{ + asset::manager::ResourceManager, + core::{ + log::Log, + pool::{Handle, PayloadContainer, Ticket}, + visitor::{Visit, VisitError, Visitor, VisitorFlags}, + }, + engine::SerializationContext, + gui::constructor::WidgetConstructorContainer, + plugin::Plugin, + scene::{ + base::visit_opt_script, + node::{container::NodeContainer, Node}, + Scene, + }, + script::Script, +}; +use std::{ops::Deref, sync::Arc}; + +pub struct ScriptState { + index: usize, + binary_blob: Vec, +} + +pub struct NodeState { + node: Handle, + ticket: Option>, + binary_blob: Vec, + scripts: Vec, +} + +pub struct SceneState { + pub scene: Handle, + nodes: Vec, +} + +impl SceneState { + pub fn try_create_from_plugin( + scene_handle: Handle, + scene: &mut Scene, + serialization_context: &SerializationContext, + plugin: &dyn Plugin, + ) -> Result, String> { + let mut scene_state = Self { + scene: scene_handle, + nodes: Default::default(), + }; + + for index in 0..scene.graph.capacity() { + let handle = scene.graph.handle_from_index(index); + let Some(node) = scene.graph.try_get_mut(handle) else { + continue; + }; + + let mut node_state = NodeState { + node: handle, + ticket: None, + binary_blob: Default::default(), + scripts: Default::default(), + }; + + if is_node_belongs_to_plugin(serialization_context, node, plugin) { + // The entire node belongs to plugin, serialize it entirely. + // Take the node out of the graph first. + let (ticket, node) = scene.graph.take_reserve(handle); + let mut container = NodeContainer::new(node); + let mut visitor = make_writing_visitor(); + container + .visit("Node", &mut visitor) + .map_err(|e| e.to_string())?; + node_state.binary_blob = visitor.save_binary_to_vec().map_err(|e| e.to_string())?; + node_state.ticket = Some(ticket); + } else { + // The node does not belong to the plugin, try to check its scripts. + for (script_index, record) in node.scripts.iter_mut().enumerate() { + if let Some(script) = record.script.as_ref() { + if is_script_belongs_to_plugin(serialization_context, script, plugin) { + // Take the script out of the node and serialize it. The script will be + // dropped and destroyed. + let mut script = record.script.take(); + let mut visitor = make_writing_visitor(); + visit_opt_script("Script", &mut script, &mut visitor) + .map_err(|e| e.to_string())?; + let binary_blob = + visitor.save_binary_to_vec().map_err(|e| e.to_string())?; + + node_state.scripts.push(ScriptState { + index: script_index, + binary_blob, + }) + } + } + } + } + + if !node_state.binary_blob.is_empty() || !node_state.scripts.is_empty() { + scene_state.nodes.push(node_state); + } + } + + if !scene_state.nodes.is_empty() { + Ok(Some(scene_state)) + } else { + Ok(None) + } + } + + pub fn deserialize_into_scene( + self, + scene: &mut Scene, + serialization_context: &Arc, + resource_manager: &ResourceManager, + widget_constructors: &Arc, + ) -> Result<(), String> { + let script_message_sender = scene.graph.script_message_sender.clone(); + + for node_state in self.nodes { + let node = &mut scene.graph[node_state.node]; + + if node_state.binary_blob.is_empty() { + // Only scripts needs to be reloaded. + for script in node_state.scripts { + let mut visitor = make_reading_visitor( + &script.binary_blob, + serialization_context, + resource_manager, + widget_constructors, + ) + .map_err(|e| e.to_string())?; + let mut opt_script: Option