diff --git a/server/src/lib.rs b/server/src/lib.rs index 065245bd..4f1e9375 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -18,6 +18,7 @@ pub mod commands; pub mod message_queue; pub mod pathext; pub mod project; +pub mod rbx_session; pub mod session; pub mod session_id; pub mod vfs; diff --git a/server/src/project.rs b/server/src/project.rs index 78244856..853c8547 100644 --- a/server/src/project.rs +++ b/server/src/project.rs @@ -12,7 +12,7 @@ pub static PROJECT_FILENAME: &'static str = "roblox-project.json"; #[derive(Debug, Serialize, Deserialize)] #[serde(untagged)] enum SourceProjectNode { - Regular { + Instance { #[serde(rename = "$className")] class_name: String, @@ -31,17 +31,17 @@ enum SourceProjectNode { impl SourceProjectNode { pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode { match self { - SourceProjectNode::Regular { class_name, mut children } => { + SourceProjectNode::Instance { class_name, mut children } => { let mut new_children = HashMap::new(); for (node_name, node) in children.drain() { new_children.insert(node_name, node.into_project_node(project_file_location)); } - ProjectNode::Regular { + ProjectNode::Instance(InstanceProjectNode { class_name, children: new_children, - } + }) }, SourceProjectNode::SyncPoint { path: source_path } => { let path = if Path::new(&source_path).is_absolute() { @@ -51,9 +51,9 @@ impl SourceProjectNode { project_folder_location.join(source_path) }; - ProjectNode::SyncPoint { + ProjectNode::SyncPoint(SyncPointProjectNode { path, - } + }) }, } } @@ -139,15 +139,21 @@ impl fmt::Display for ProjectSaveError { #[derive(Debug)] pub enum ProjectNode { - Regular { - class_name: String, - children: HashMap, - // properties: HashMap, - // ignore_unknown: bool, - }, - SyncPoint { - path: PathBuf, - }, + Instance(InstanceProjectNode), + SyncPoint(SyncPointProjectNode), +} + +#[derive(Debug)] +pub struct InstanceProjectNode { + pub class_name: String, + pub children: HashMap, + // properties: HashMap, + // ignore_unknown: bool, +} + +#[derive(Debug)] +pub struct SyncPointProjectNode { + pub path: PathBuf, } #[derive(Debug)] diff --git a/server/src/rbx_session.rs b/server/src/rbx_session.rs new file mode 100644 index 00000000..427f490e --- /dev/null +++ b/server/src/rbx_session.rs @@ -0,0 +1,160 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Mutex}, + str, +}; + +use rbx_tree::{RbxTree, RbxId, RbxInstance, RbxValue}; + +use crate::{ + project::{Project, ProjectNode, InstanceProjectNode}, + message_queue::MessageQueue, + vfs::{Vfs, VfsItem}, +}; + +pub struct RbxSession { + tree: RbxTree, + paths_to_ids: HashMap, + message_queue: Arc, + vfs: Arc>, + project: Arc, +} + +impl RbxSession { + pub fn new(project: Arc, vfs: Arc>, message_queue: Arc) -> RbxSession { + let (tree, paths_to_ids) = { + let temp_vfs = vfs.lock().unwrap(); + construct_initial_tree(&project, &temp_vfs) + }; + + { + use serde_json; + println!("{}", serde_json::to_string(&tree).unwrap()); + } + + RbxSession { + tree, + paths_to_ids, + message_queue, + vfs, + project, + } + } + + pub fn path_created_or_updated(&mut self, path: &Path) { + println!("Path changed: {}", path.display()); + } + + pub fn path_removed(&mut self, path: &Path) { + println!("Path removed: {}", path.display()); + } + + pub fn path_renamed(&mut self, from_path: &Path, to_path: &Path) { + println!("Path renamed from {} to {}", from_path.display(), to_path.display()); + } + + pub fn get_tree(&self) -> &RbxTree { + &self.tree + } +} + +fn construct_initial_tree( + project: &Project, + vfs: &Vfs, +) -> (RbxTree, HashMap) { + let mut paths_to_ids = HashMap::new(); + let mut tree = RbxTree::new(RbxInstance { + name: "this isn't supposed to be here".to_string(), + class_name: "ahhh, help me".to_string(), + properties: HashMap::new(), + }); + + let root_id = tree.get_root_id(); + + construct_initial_tree_node(&mut tree, vfs, &mut paths_to_ids, root_id, "<<>>", &project.tree); + + (tree, paths_to_ids) +} + +fn construct_initial_tree_node( + tree: &mut RbxTree, + vfs: &Vfs, + paths_to_ids: &mut HashMap, + parent_instance_id: RbxId, + instance_name: &str, + project_node: &ProjectNode, +) { + match project_node { + ProjectNode::Instance(node) => { + construct_instance_node(tree, vfs, paths_to_ids, parent_instance_id, instance_name, node); + }, + ProjectNode::SyncPoint(node) => { + construct_sync_point_node(tree, vfs, paths_to_ids, parent_instance_id, instance_name, &node.path); + }, + } +} + +fn construct_instance_node( + tree: &mut RbxTree, + vfs: &Vfs, + paths_to_ids: &mut HashMap, + parent_instance_id: RbxId, + instance_name: &str, + project_node: &InstanceProjectNode, +) { + let instance = RbxInstance { + class_name: project_node.class_name.clone(), + name: instance_name.to_string(), + properties: HashMap::new(), + }; + + let id = tree.insert_instance(instance, parent_instance_id); + + for (child_name, child_project_node) in &project_node.children { + construct_initial_tree_node(tree, vfs, paths_to_ids, id, child_name, child_project_node); + } +} + +fn construct_sync_point_node( + tree: &mut RbxTree, + vfs: &Vfs, + paths_to_ids: &mut HashMap, + parent_instance_id: RbxId, + instance_name: &str, + file_path: &Path, +) { + match vfs.get(&file_path) { + Some(VfsItem::File(file)) => { + let contents = str::from_utf8(vfs.get_contents(&file.path).unwrap()).unwrap(); + + let mut properties = HashMap::new(); + properties.insert("Source".to_string(), RbxValue::String { value: contents.to_string() }); + + let instance = RbxInstance { + class_name: "ModuleScript".to_string(), + name: instance_name.to_string(), + properties, + }; + + let id = tree.insert_instance(instance, parent_instance_id); + paths_to_ids.insert(file.path.clone(), id); + }, + Some(VfsItem::Directory(directory)) => { + let instance = RbxInstance { + class_name: "Folder".to_string(), + name: instance_name.to_string(), + properties: HashMap::new(), + }; + + let id = tree.insert_instance(instance, parent_instance_id); + paths_to_ids.insert(directory.path.clone(), id); + + for child_path in &directory.children { + let child_instance_name = child_path.file_name().unwrap().to_str().unwrap(); + construct_sync_point_node(tree, vfs, paths_to_ids, id, child_instance_name, child_path); + } + }, + None => panic!("Couldn't read {} from disk", file_path.display()), + } +} \ No newline at end of file diff --git a/server/src/session.rs b/server/src/session.rs index ff1c3724..53533dba 100644 --- a/server/src/session.rs +++ b/server/src/session.rs @@ -1,15 +1,10 @@ use std::{ - collections::HashMap, - sync::{Arc, RwLock, Mutex, mpsc}, - path::{Path, PathBuf}, + sync::{Arc, Mutex, mpsc}, thread, io, time::Duration, - str, }; -use serde_json; - use notify::{ self, DebouncedEvent, @@ -18,142 +13,66 @@ use notify::{ Watcher, }; -use rbx_tree::{RbxId, RbxTree, RbxInstance, RbxValue}; - use crate::{ message_queue::MessageQueue, project::{Project, ProjectNode}, - vfs::{Vfs, VfsItem}, + vfs::Vfs, session_id::SessionId, + rbx_session::RbxSession, }; const WATCH_TIMEOUT_MS: u64 = 100; pub struct Session { - project: Project, + project: Arc, pub session_id: SessionId, pub message_queue: Arc, - pub tree: Arc>, - paths_to_ids: HashMap, + pub rbx_session: Arc>, vfs: Arc>, watchers: Vec, } fn add_sync_points(vfs: &mut Vfs, project_node: &ProjectNode) -> io::Result<()> { match project_node { - ProjectNode::Regular { children, .. } => { - for child in children.values() { + ProjectNode::Instance(node) => { + for child in node.children.values() { add_sync_points(vfs, child)?; } }, - ProjectNode::SyncPoint { path } => { - vfs.add_root(path)?; + ProjectNode::SyncPoint(node) => { + vfs.add_root(&node.path)?; }, } Ok(()) } -fn read_sync_to_rbx( - tree: &mut RbxTree, - vfs: &Vfs, - paths_to_ids: &mut HashMap, - parent_node_id: RbxId, - project_node_name: &str, - path: &Path -) { - match vfs.get(path) { - Some(VfsItem::File(file)) => { - let contents = str::from_utf8(vfs.get_contents(&file.path).unwrap()).unwrap(); - - let mut properties = HashMap::new(); - properties.insert("Source".to_string(), RbxValue::String { value: contents.to_string() }); - - let instance = RbxInstance { - class_name: "ModuleScript".to_string(), - name: project_node_name.to_string(), - properties, - }; - - let id = tree.insert_instance(instance, parent_node_id); - paths_to_ids.insert(path.to_path_buf(), id); - }, - Some(VfsItem::Directory(directory)) => { - let instance = RbxInstance { - class_name: "Folder".to_string(), - name: project_node_name.to_string(), - properties: HashMap::new(), - }; - - let id = tree.insert_instance(instance, parent_node_id); - paths_to_ids.insert(path.to_path_buf(), id); - - for child_path in &directory.children { - let child_name = child_path.file_name().unwrap().to_str().unwrap(); - read_sync_to_rbx(tree, vfs, paths_to_ids, id, child_name, child_path); - } - }, - None => panic!("Couldn't read {} from disk", path.display()), - } -} - -fn read_to_rbx( - tree: &mut RbxTree, - vfs: &Vfs, - paths_to_ids: &mut HashMap, - parent_node_id: RbxId, - project_node_name: &str, - project_node: &ProjectNode -) { - match project_node { - ProjectNode::Regular { children, class_name, .. } => { - let instance = RbxInstance { - class_name: class_name.clone(), - name: project_node_name.to_string(), - properties: HashMap::new(), - }; - - let id = tree.insert_instance(instance, parent_node_id); - - for (child_name, child_project_node) in children { - read_to_rbx(tree, vfs, paths_to_ids, id, child_name, child_project_node); - } - }, - ProjectNode::SyncPoint { path } => { - read_sync_to_rbx(tree, vfs, paths_to_ids, parent_node_id, project_node_name, path); - }, - } -} - impl Session { pub fn new(project: Project) -> io::Result { - let mut vfs = Vfs::new(); + let project = Arc::new(project); + let message_queue = Arc::new(MessageQueue::new()); + let vfs = Arc::new(Mutex::new(Vfs::new())); - let (change_tx, change_rx) = mpsc::channel(); + { + let mut vfs = vfs.lock().unwrap(); + add_sync_points(&mut vfs, &project.tree) + .expect("Could not add sync points when starting new Rojo session"); + } - add_sync_points(&mut vfs, &project.tree) - .expect("Could not add sync points when starting new Rojo session"); + let rbx_session = Arc::new(Mutex::new(RbxSession::new( + Arc::clone(&project), + Arc::clone(&vfs), + Arc::clone(&message_queue), + ))); - let mut tree = RbxTree::new(RbxInstance { - name: "ahhhh".to_string(), - class_name: "ahhh help me".to_string(), - properties: HashMap::new(), - }); - - let mut paths_to_ids = HashMap::new(); - - let root_id = tree.get_root_id(); - read_to_rbx(&mut tree, &vfs, &mut paths_to_ids, root_id, "root", &project.tree); - - println!("tree:\n{}", serde_json::to_string(&tree).unwrap()); - - let vfs = Arc::new(Mutex::new(vfs)); let mut watchers = Vec::new(); { let vfs_temp = vfs.lock().unwrap(); for root in vfs_temp.get_roots() { + println!("Watching {}", root.display()); + let (watch_tx, watch_rx) = mpsc::channel(); let mut watcher = notify::watcher(watch_tx, Duration::from_millis(WATCH_TIMEOUT_MS)).unwrap(); @@ -163,8 +82,8 @@ impl Session { watchers.push(watcher); - let change_tx = change_tx.clone(); let vfs = Arc::clone(&vfs); + let rbx_session = Arc::clone(&rbx_session); thread::spawn(move || { loop { @@ -172,23 +91,40 @@ impl Session { Ok(event) => { match event { DebouncedEvent::Create(path) | DebouncedEvent::Write(path) => { - let mut vfs = vfs.lock().unwrap(); - vfs.add_or_update(&path).unwrap(); - change_tx.send(path.clone()).unwrap(); + { + let mut vfs = vfs.lock().unwrap(); + vfs.add_or_update(&path).unwrap(); + } + + { + let mut rbx_session = rbx_session.lock().unwrap(); + rbx_session.path_created_or_updated(&path); + } }, DebouncedEvent::Remove(path) => { - let mut vfs = vfs.lock().unwrap(); - vfs.remove(&path); - change_tx.send(path.clone()).unwrap(); + { + let mut vfs = vfs.lock().unwrap(); + vfs.remove(&path); + } + + { + let mut rbx_session = rbx_session.lock().unwrap(); + rbx_session.path_removed(&path); + } }, DebouncedEvent::Rename(from_path, to_path) => { - let mut vfs = vfs.lock().unwrap(); - vfs.remove(&from_path); - vfs.add_or_update(&to_path).unwrap(); - change_tx.send(from_path.clone()).unwrap(); - change_tx.send(to_path.clone()).unwrap(); + { + let mut vfs = vfs.lock().unwrap(); + vfs.remove(&from_path); + vfs.add_or_update(&to_path).unwrap(); + } + + { + let mut rbx_session = rbx_session.lock().unwrap(); + rbx_session.path_renamed(&from_path, &to_path); + } }, - _ => continue, + _ => {}, }; }, Err(_) => break, @@ -199,17 +135,15 @@ impl Session { } } - let message_queue = Arc::new(MessageQueue::new()); let session_id = SessionId::new(); Ok(Session { session_id, - paths_to_ids, + rbx_session, project, message_queue, - tree: Arc::new(RwLock::new(tree)), vfs, - watchers: Vec::new(), + watchers, }) } diff --git a/server/src/web.rs b/server/src/web.rs index 4f19e39c..150c019f 100644 --- a/server/src/web.rs +++ b/server/src/web.rs @@ -63,7 +63,8 @@ impl Server { (GET) (/api/rojo) => { // Get a summary of information about the server. - let tree = self.session.tree.read().unwrap(); + let rbx_session = self.session.rbx_session.lock().unwrap(); + let tree = rbx_session.get_tree(); Response::json(&ServerInfoResponse { server_version: self.server_version, @@ -127,7 +128,8 @@ impl Server { None => return rouille::Response::text("Malformed ID list").with_status_code(400), }; - let tree = self.session.tree.read().unwrap(); + let rbx_session = self.session.rbx_session.lock().unwrap(); + let tree = rbx_session.get_tree(); let message_cursor = message_queue.get_message_cursor();